├── spec ├── internal │ ├── public │ │ └── favicon.ico │ ├── app │ │ ├── models │ │ │ ├── user.rb │ │ │ ├── order │ │ │ │ ├── blank.rb │ │ │ │ ├── normal_invalid.rb │ │ │ │ ├── normal.rb │ │ │ │ └── oneclick.rb │ │ │ └── concerns │ │ │ │ ├── universally_unique_identifiable.rb │ │ │ │ └── orderable.rb │ │ ├── views │ │ │ ├── normal_orders │ │ │ │ ├── failed.html.erb │ │ │ │ ├── new.html.erb │ │ │ │ ├── gateway.html.erb │ │ │ │ └── success.html.erb │ │ │ ├── oneclick_orders │ │ │ │ ├── failed.html.erb │ │ │ │ ├── new.html.erb │ │ │ │ └── success.html.erb │ │ │ └── oneclick_inscriptions │ │ │ │ ├── failed.html.erb │ │ │ │ ├── new.html.erb │ │ │ │ ├── gateway.html.erb │ │ │ │ └── success.html.erb │ │ └── controllers │ │ │ ├── oneclick_inscriptions_controller.rb │ │ │ ├── oneclick_orders_controller.rb │ │ │ └── normal_orders_controller.rb │ ├── config │ │ ├── database.yml │ │ └── routes.rb │ ├── db │ │ └── schema.rb │ └── vendor │ │ └── vault │ │ ├── tbk.pem │ │ ├── 597020000541.crt │ │ ├── 597020000547.crt │ │ ├── 597020000541.key │ │ └── 597020000547.key ├── webpay_rails_spec.rb ├── spec_helper.rb ├── nullify_spec.rb ├── oneclick_spec.rb ├── vault_helper.rb └── normal_spec.rb ├── .rspec ├── lib ├── webpay_rails │ ├── version.rb │ ├── railites.rb │ ├── responses │ │ ├── init_inscription.rb │ │ ├── reverse.rb │ │ ├── remove_user.rb │ │ ├── init_transaction.rb │ │ ├── transaction_nullify.rb │ │ ├── finish_inscription.rb │ │ ├── authorization.rb │ │ └── transaction_result.rb │ ├── response.rb │ ├── soap_nullify.rb │ ├── vault.rb │ ├── soap_normal.rb │ ├── errors.rb │ ├── verifier.rb │ ├── soap_oneclick.rb │ ├── soap.rb │ └── base.rb └── webpay_rails.rb ├── CHANGELOG.md ├── config.ru ├── .gitignore ├── Rakefile ├── .travis.yml ├── Gemfile ├── MIT-LICENSE ├── webpay_rails.gemspec └── README.md /spec/internal/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /spec/internal/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | end 3 | -------------------------------------------------------------------------------- /spec/internal/app/views/normal_orders/failed.html.erb: -------------------------------------------------------------------------------- 1 |

Failed transaction

2 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_orders/failed.html.erb: -------------------------------------------------------------------------------- 1 |

Failed transaction

2 | -------------------------------------------------------------------------------- /lib/webpay_rails/version.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | VERSION = '1.1.1'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_inscriptions/failed.html.erb: -------------------------------------------------------------------------------- 1 |

Failed inscription

2 | -------------------------------------------------------------------------------- /spec/internal/config/database.yml: -------------------------------------------------------------------------------- 1 | test: 2 | adapter: sqlite3 3 | database: db/combustion_test.sqlite3 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | See the [Releases section of our GitHub project](https://github.com/limcross/webpay_rails/releases) for changelogs for each release version of WebpayRails. 2 | -------------------------------------------------------------------------------- /lib/webpay_rails/railites.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class Railties < ::Rails::Railtie 3 | initializer 'Rails logger' do 4 | WebpayRails.rails_logger = Rails.logger 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | 4 | Bundler.require :default, :development 5 | 6 | Combustion.initialize! :active_record, :action_controller, :action_view 7 | run Combustion::Application 8 | -------------------------------------------------------------------------------- /spec/internal/app/models/order/blank.rb: -------------------------------------------------------------------------------- 1 | class Order::Blank < ActiveRecord::Base 2 | extend WebpayRails 3 | include Orderable 4 | include UniversallyUniqueIdentifiable 5 | 6 | self.table_name = 'orders' 7 | end 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | spec/internal/log/* 2 | spec/internal/tmp/* 3 | spec/internal/db/*.sqlite3 4 | spec/internal/db/log/*.log 5 | *~ 6 | coverage/* 7 | *.sqlite3 8 | .bundle 9 | rdoc/* 10 | pkg 11 | log 12 | test/tmp/* 13 | *.gem 14 | Gemfile.lock 15 | -------------------------------------------------------------------------------- /spec/internal/app/views/normal_orders/new.html.erb: -------------------------------------------------------------------------------- 1 |

Form

2 | 3 | <%= form_for @order, url: url_for(action: :create, controller: :normal_orders) do |f| %> 4 | <%= f.number_field :amount %> 5 | <%= f.submit 'Purchase' %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_orders/new.html.erb: -------------------------------------------------------------------------------- 1 |

Form

2 | 3 | <%= form_for @order, url: url_for(action: :create, controller: :oneclick_orders) do |f| %> 4 | <%= f.number_field :amount %> 5 | <%= f.submit 'Purchase' %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/init_inscription.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class InitInscription < WebpayRails::Response 3 | def self.attr_list 4 | [:token, :url_webpay] 5 | end 6 | 7 | attr_accessor(*attr_list) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_inscriptions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Form

2 | 3 | <%= form_for @user, url: url_for(action: :create, controller: :oneclick_inscriptions) do |f| %> 4 | <%= f.text_field :email %> 5 | <%= f.text_field :username %> 6 | <%= f.submit 'Inscribe' %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/reverse.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class Reverse < WebpayRails::Response 3 | def self.attr_list 4 | [:return] 5 | end 6 | 7 | attr_accessor(*attr_list) 8 | 9 | def success? 10 | self.return == 'true' 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/internal/app/models/concerns/universally_unique_identifiable.rb: -------------------------------------------------------------------------------- 1 | module UniversallyUniqueIdentifiable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | before_create :set_uuid 6 | end 7 | 8 | def set_uuid 9 | assign_attributes(uuid: SecureRandom.uuid) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/remove_user.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class RemoveUser < WebpayRails::Response 3 | def self.attr_list 4 | [:return] 5 | end 6 | 7 | attr_accessor(*attr_list) 8 | 9 | def success? 10 | self.return == 'true' 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/setup' 3 | rescue LoadError 4 | puts 'You must `gem install bundler` and `bundle install` to run rake tasks' 5 | end 6 | 7 | require "rspec/core/rake_task" 8 | 9 | desc "Run all examples" 10 | RSpec::Core::RakeTask.new(:spec) do |t| 11 | t.rspec_opts = %w[--color] 12 | end 13 | 14 | task default: :spec 15 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/init_transaction.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | module Responses 3 | class InitTransaction < WebpayRails::Response 4 | def self.attr_list 5 | [:token, :url] 6 | end 7 | 8 | attr_accessor(*attr_list) 9 | 10 | def success? 11 | !token.blank? 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/internal/app/views/normal_orders/gateway.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_tag(@url, method: @method, style: 'display: none;', id: 'gatewayForm', enforce_utf8: false, authenticity_token: false) do %> 2 | <%= hidden_field_tag(:token_ws, @token) %> 3 | <%= submit_tag("Continuar a webpay") %> 4 | <% end %> 5 | 6 | 9 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_inscriptions/gateway.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_tag(@url, method: @method, style: 'display: none;', id: 'gatewayForm', enforce_utf8: false, authenticity_token: false) do %> 2 | <%= hidden_field_tag(:TBK_TOKEN, @token) %> 3 | <%= submit_tag("Continuar a webpay") %> 4 | <% end %> 5 | 6 | 9 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/transaction_nullify.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class TransactionNullify < WebpayRails::Response 3 | def self.attr_list 4 | [:token, :authorization_code, :authorization_date, :balance, 5 | :nullified_amount] 6 | end 7 | 8 | attr_accessor(*attr_list) 9 | 10 | def success? 11 | !token.blank? 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/finish_inscription.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class FinishInscription < WebpayRails::Response 3 | def self.attr_list 4 | [:response_code, :auth_code, :tbk_user, :last_4_card_digits, 5 | :credit_card_type] 6 | end 7 | 8 | attr_accessor(*attr_list) 9 | 10 | def success? 11 | response_code.to_i.zero? 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/internal/app/models/concerns/orderable.rb: -------------------------------------------------------------------------------- 1 | module Orderable 2 | extend ActiveSupport::Concern 3 | 4 | included do 5 | enum status: [:created, :approved, :failed, :canceled, :expired, :pending, 6 | :refunded] 7 | 8 | scope :approved, -> { where(status: self.statuses[:approved]) } 9 | scope :normal_selling, -> { where(tbk_payment_type_code: 'VN') } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/authorization.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class Authorization < WebpayRails::Response 3 | def self.attr_list 4 | [:response_code, :authorization_code, :transaction_id, 5 | :last_4_card_digits, :credit_card_type] 6 | end 7 | 8 | attr_accessor(*attr_list) 9 | 10 | def success? 11 | response_code.to_i.zero? 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_inscriptions/success.html.erb: -------------------------------------------------------------------------------- 1 |

Success inscription

2 | 3 |

response_code: <%= @response.response_code %>

4 |

auth_code: <%= @response.auth_code %>

5 |

tbk_user: <%= @response.tbk_user %>

6 |

last_4_card_digits: <%= @response.last_4_card_digits %>

7 |

credit_card_type: <%= @response.credit_card_type %>

8 | -------------------------------------------------------------------------------- /lib/webpay_rails/response.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class Response 3 | def self.attr_list 4 | [] 5 | end 6 | 7 | def initialize(response) 8 | document = Nokogiri::HTML(response.to_s) 9 | self.class.attr_list.each do |k| 10 | v = document.at_xpath("//#{k.to_s.tr('_', '')}") 11 | send("#{k}=", v.text.to_s) unless v.nil? 12 | end 13 | end 14 | 15 | attr_accessor(*attr_list) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/internal/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root to: 'normal_orders#new' 3 | 4 | resources :normal_orders, only: [:new, :create] do 5 | member do 6 | post :return 7 | post :final 8 | end 9 | end 10 | 11 | resources :oneclick_inscriptions, only: [:new, :create] do 12 | collection do 13 | post :finish 14 | end 15 | end 16 | 17 | resources :oneclick_orders, only: [:new, :create] 18 | end 19 | -------------------------------------------------------------------------------- /spec/internal/app/views/oneclick_orders/success.html.erb: -------------------------------------------------------------------------------- 1 |

Success transaction

2 | 3 |

tbk_response_code: <%= @response.response_code %>

4 |

tbk_authorization_code: <%= @response.authorization_code %>

5 |

tbk_transaction_id: <%= @response.transaction_id %>

6 |

tbk_last_4_card_digits: <%= @response.last_4_card_digits %>

7 |

tbk_credit_card_type: <%= @response.credit_card_type %>

8 | -------------------------------------------------------------------------------- /spec/internal/app/models/order/normal_invalid.rb: -------------------------------------------------------------------------------- 1 | class Order::NormalInvalid < ActiveRecord::Base 2 | extend WebpayRails 3 | include Orderable 4 | include UniversallyUniqueIdentifiable 5 | 6 | self.table_name = 'orders' 7 | 8 | webpay_rails( 9 | commerce_code: 597020000541, 10 | private_key: Rails.root.join('vendor/vault/597020000541.key').to_s, 11 | public_cert: Rails.root.join('vendor/vault/597020000541.crt').to_s, 12 | webpay_cert: Rails.root.join('vendor/vault/597020000541.crt').to_s 13 | ) 14 | end 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | sudo: required 4 | dist: trusty 5 | before_install: 6 | - sudo apt-get update 7 | - sudo apt-get install chromium-chromedriver 8 | before_script: 9 | - "export PATH=$PATH:/usr/lib/chromium-browser/" 10 | - "export DISPLAY=:99.0" 11 | - "sh -e /etc/init.d/xvfb start" 12 | - sleep 3 # give xvfb some time to start 13 | rvm: 14 | - 2.2.2 15 | - 2.3.0 16 | env: 17 | - CAPYBARA_DRIVER=headless_chrome 18 | after_success: 19 | - coveralls 20 | notifications: 21 | email: false 22 | -------------------------------------------------------------------------------- /spec/internal/app/models/order/normal.rb: -------------------------------------------------------------------------------- 1 | class Order::Normal < ActiveRecord::Base 2 | extend WebpayRails 3 | include Orderable 4 | include UniversallyUniqueIdentifiable 5 | 6 | self.table_name = 'orders' 7 | 8 | webpay_rails( 9 | commerce_code: 597020000541, 10 | private_key: Rails.root.join('vendor/vault/597020000541.key').to_s, 11 | public_cert: Rails.root.join('vendor/vault/597020000541.crt').to_s, 12 | webpay_cert: Rails.root.join('vendor/vault/tbk.pem').to_s 13 | ) 14 | 15 | def buy_order_for_transbank_normal 16 | uuid.first(30).delete!('-') 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Declare your gem's dependencies in secret_id.gemspec. 4 | # Bundler will treat runtime dependencies like base dependencies, and 5 | # development dependencies will be added by default to the :development group. 6 | gemspec 7 | 8 | # Declare any dependencies that are still in development here instead of in 9 | # your gemspec. These might include edge Rails or gems from your path or 10 | # Git. Remember to move these dependencies to your gemspec before releasing 11 | # your gem to rubygems.org. 12 | 13 | # To use a debugger 14 | # gem 'byebug', group: [:development, :test] 15 | -------------------------------------------------------------------------------- /lib/webpay_rails/responses/transaction_result.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails::Responses 2 | class TransactionResult < WebpayRails::Response 3 | def self.attr_list 4 | [ 5 | :buy_order, :session_id, :accounting_date, :transaction_date, :vci, 6 | :url_redirection, 7 | 8 | # card details 9 | :card_number, :card_expiration_date, 10 | 11 | # transaction details 12 | :authorization_code, :payment_type_code, :response_code, 13 | :amount, :shares_number, :commerce_code 14 | ] 15 | end 16 | 17 | attr_accessor(*attr_list) 18 | 19 | def approved? 20 | response_code.to_i.zero? 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/internal/db/schema.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Schema.define do 2 | create_table(:orders, force: true) do |t| 3 | t.string :uuid 4 | t.string :tbk_token_ws, index: true 5 | t.string :tbk_accounting_date 6 | t.string :tbk_buy_order 7 | t.string :tbk_card_number 8 | t.string :tbk_commerce_code 9 | t.string :tbk_authorization_code 10 | t.string :tbk_payment_type_code 11 | t.string :tbk_response_code 12 | t.string :tbk_transaction_date 13 | t.string :tbk_vci 14 | t.string :tbk_session_id 15 | t.string :tbk_card_expiration_date 16 | t.string :tbk_shares_number 17 | 18 | t.string :tbk_transaction_id 19 | t.string :tbk_credit_card_type 20 | 21 | t.integer :amount 22 | t.integer :status, default: 0 23 | 24 | t.timestamps 25 | end 26 | 27 | create_table(:users, force: true) do |t| 28 | t.string :username 29 | t.string :email 30 | t.string :tbk_user 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/webpay_rails/soap_nullify.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class SoapNullify < Soap 3 | def nullify(args) 4 | request = client.build_request(:nullify, message: nullify_message(args)) 5 | 6 | call(request, :nullify) 7 | end 8 | 9 | private 10 | 11 | def wsdl_path 12 | case @environment 13 | when :production 14 | 'https://webpay3g.transbank.cl/WSWebpayTransaction/cxf/WSCommerceIntegrationService?wsdl' 15 | when :certification, :integration 16 | 'https://webpay3gint.transbank.cl/WSWebpayTransaction/cxf/WSCommerceIntegrationService?wsdl' 17 | end 18 | end 19 | 20 | def nullify_message(args) 21 | { 22 | nullificationInput: { 23 | authorizationCode: args[:authorization_code], 24 | authorizedAmount: args[:authorized_amount], 25 | buyOrder: args[:buy_order], 26 | commerceId: @commerce_code, 27 | nullifyAmount: args[:nullify_amount] 28 | } 29 | } 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/webpay_rails/vault.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class Vault 3 | def initialize(args) 4 | args.map { |k, v| send("#{k}=", v) if respond_to? k } 5 | 6 | raise WebpayRails::MissingPrivateKey unless @private_key 7 | raise WebpayRails::MissingWebpayCertificate unless @webpay_cert 8 | raise WebpayRails::MissingPublicCertificate unless @public_cert 9 | end 10 | 11 | attr_reader :webpay_cert, :private_key, :public_cert 12 | 13 | private 14 | 15 | def webpay_cert=(cert) 16 | @webpay_cert ||= OpenSSL::X509::Certificate.new(read(cert)) 17 | end 18 | 19 | def private_key=(key) 20 | @private_key ||= OpenSSL::PKey::RSA.new(read(key)) 21 | end 22 | 23 | def public_cert=(cert) 24 | @public_cert ||= OpenSSL::X509::Certificate.new(read(cert)) 25 | end 26 | 27 | def read(val) 28 | return val if val.include? '-----BEGIN' 29 | 30 | path = Pathname.new(val) 31 | return path.read if path.file? 32 | 33 | raise WebpayRails::FileNotFound, val 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/internal/app/models/order/oneclick.rb: -------------------------------------------------------------------------------- 1 | class Order::Oneclick < ActiveRecord::Base 2 | extend WebpayRails 3 | include Orderable 4 | include UniversallyUniqueIdentifiable 5 | 6 | self.table_name = 'orders' 7 | 8 | webpay_rails( 9 | commerce_code: 597020000547, 10 | private_key: Rails.root.join('vendor/vault/597020000547.key').to_s, 11 | public_cert: Rails.root.join('vendor/vault/597020000547.crt').to_s, 12 | webpay_cert: Rails.root.join('vendor/vault/tbk.pem').to_s 13 | ) 14 | 15 | scope :created_on, ->(at) { where(created_at: at) } 16 | 17 | # Identificador único de la compra generado por el comercio. 18 | # Debe ser timestamp [yyyymmddhhMMss] + un correlativo de tres dígitos. 19 | # Ej: Para la tercera transacción realizada el día 15 de julio de 2011 a las 20 | # 11:55:50 la orden de compra sería: 20110715115550003. 21 | def buy_order_for_transbank_oneclick 22 | order = Order::Oneclick.created_on(created_at).first! 23 | time = created_at.strftime("%Y%m%d%H%M%S") 24 | offset = (id - order.id + 1).to_s.rjust(3, '0') 25 | time + offset 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Sebastián Orellana 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/internal/vendor/vault/tbk.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDKTCCAhECBFZl7uIwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ0wxDjAMBgNVBAgMBUNo 3 | aWxlMREwDwYDVQQHDAhTYW50aWFnbzEMMAoGA1UECgwDa2R1MQwwCgYDVQQLDANrZHUxCzAJBgNV 4 | BAMMAjEwMB4XDTE1MTIwNzIwNDEwNloXDTE4MDkwMjIwNDEwNlowWTELMAkGA1UEBhMCQ0wxDjAM 5 | BgNVBAgMBUNoaWxlMREwDwYDVQQHDAhTYW50aWFnbzEMMAoGA1UECgwDa2R1MQwwCgYDVQQLDANr 6 | ZHUxCzAJBgNVBAMMAjEwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAizJUWTDC7nfP 7 | 3jmZpWXFdG9oKyBrU0Bdl6fKif9a1GrwevThsU5Dq3wiRfYvomStNjFDYFXOs9pRIxqX2AWDybjA 8 | X/+bdDTVbM+xXllA9stJY8s7hxAvwwO7IEuOmYDpmLKP7J+4KkNH7yxsKZyLL9trG3iSjV6Y6SO5 9 | EEhUsdxoJFAow/h7qizJW0kOaWRcljf7kpqJAL3AadIuqV+hlf+Ts/64aMsfSJJA6xdbdp9ddgVF 10 | oqUl1M8vpmd4glxlSrYmEkbYwdI9uF2d6bAeaneBPJFZr6KQqlbbrVyeJZqmMlEPy0qPco1TIxrd 11 | EHlXgIFJLyyMRAyjX9i4l70xjwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBn3tUPS6e2USgMrPKp 12 | sxU4OTfW64+mfD6QrVeBOh81f6aGHa67sMJn8FE/cG6jrUmX/FP1/Cpbpvkm5UUlFKpgaFfHv+Kg 13 | CpEvgcRIv/OeIi6Jbuu3NrPdGPwzYkzlOQnmgio5RGb6GSs+OQ0mUWZ9J1+YtdZc+xTga0x7nsCT 14 | 5xNcUXsZKhyjoKhXtxJm3eyB3ysLNyuL/RHy/EyNEWiUhvt1SIePnW+Y4/cjQWYwNqSqMzTSW9TP 15 | 2QR2bX/W2H6ktRcLsgBK9mq7lE36p3q6c9DtZJE+xfA4NGCYWM9hd8pbusnoNO7AFxJZOuuvLZI7 16 | JvD7YLhPvCYKry7N6x3l 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /lib/webpay_rails.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'active_support/concern' 3 | 4 | require 'signer' 5 | require 'savon' 6 | require 'nokogiri' 7 | require 'base64' 8 | require 'digest/sha1' 9 | require 'openssl' 10 | 11 | require 'webpay_rails/version' 12 | require 'webpay_rails/errors' 13 | require 'webpay_rails/vault' 14 | require 'webpay_rails/soap' 15 | require 'webpay_rails/soap_normal' 16 | require 'webpay_rails/soap_nullify' 17 | require 'webpay_rails/soap_oneclick' 18 | require 'webpay_rails/verifier' 19 | require 'webpay_rails/response' 20 | require 'webpay_rails/responses/init_transaction' 21 | require 'webpay_rails/responses/transaction_result' 22 | require 'webpay_rails/responses/transaction_nullify' 23 | require 'webpay_rails/responses/init_inscription' 24 | require 'webpay_rails/responses/finish_inscription' 25 | require 'webpay_rails/responses/authorization' 26 | require 'webpay_rails/responses/reverse' 27 | require 'webpay_rails/responses/remove_user' 28 | require 'webpay_rails/railites' 29 | 30 | module WebpayRails 31 | autoload :Base, 'webpay_rails/base' 32 | 33 | class << self 34 | attr_accessor :rails_logger 35 | end 36 | 37 | def self.extended(base) 38 | base.include WebpayRails::Base 39 | 40 | super 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/internal/app/views/normal_orders/success.html.erb: -------------------------------------------------------------------------------- 1 |

Success transaction

2 | 3 |

id: <%= @order.id %>

4 |

tbk_token_ws: <%= @order.tbk_token_ws %>

5 |

tbk_accounting_date: <%= @order.tbk_accounting_date %>

6 |

tbk_buy_order: <%= @order.tbk_buy_order %>

7 |

tbk_card_number: <%= @order.tbk_card_number %>

8 |

tbk_commerce_code: <%= @order.tbk_commerce_code %>

9 |

tbk_authorization_code: <%= @order.tbk_authorization_code %>

10 |

tbk_payment_type_code: <%= @order.tbk_payment_type_code %>

11 |

tbk_response_code: <%= @order.tbk_response_code %>

12 |

tbk_transaction_date: <%= @order.tbk_transaction_date %>

13 |

tbk_vci: <%= @order.tbk_vci %>

14 |

tbk_session_id: <%= @order.tbk_session_id %>

15 |

tbk_card_expiration_date: <%= @order.tbk_card_expiration_date %>

16 |

tbk_shares_number: <%= @order.tbk_shares_number %>

17 |

amount: <%= @order.amount %>

18 |

status: <%= @order.status %>

19 | -------------------------------------------------------------------------------- /spec/internal/app/controllers/oneclick_inscriptions_controller.rb: -------------------------------------------------------------------------------- 1 | class OneclickInscriptionsController < ActionController::Base 2 | def new 3 | @user = User.new 4 | end 5 | 6 | def create 7 | @user = User.create(user_params) 8 | @response = Order::Oneclick.init_inscription(init_inscription_params) 9 | 10 | @method = :post 11 | @url = @response.url_webpay 12 | @token = @response.token 13 | 14 | render :gateway 15 | rescue WebpayRails::SoapError 16 | render :failed 17 | end 18 | 19 | def finish 20 | @user = User.find(params[:user_id]) 21 | @response = Order::Oneclick.finish_inscription(finish_inscription_params) 22 | if @response.success? 23 | @user.update(tbk_user: @response.tbk_user) 24 | render :success 25 | else 26 | render :failed 27 | end 28 | rescue WebpayRails::SoapError 29 | render :failed 30 | end 31 | 32 | private 33 | 34 | def user_params 35 | params.require(:user).permit(:username, :email) 36 | end 37 | 38 | def init_inscription_params 39 | user_params.merge(return_url: url_for(action: :finish, user_id: @user.id)) 40 | end 41 | 42 | def finish_inscription_params 43 | { 44 | token: params[:TBK_TOKEN] 45 | } 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/webpay_rails_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'vault_helper' 3 | 4 | describe WebpayRails do 5 | before(:all) { @webpay_rails_params = { commerce_code: COMMERCE_CODE, 6 | private_key: PRIVATE_KEY, 7 | webpay_cert: WEBPAY_CERT, 8 | public_cert: PUBLIC_CERT } } 9 | 10 | describe WebpayRails::Base do 11 | it { expect{ Order::Blank.webpay_rails(@webpay_rails_params.except(:commerce_code)) }.to raise_error(WebpayRails::MissingCommerceCode) } 12 | it { expect{ Order::Blank.webpay_rails(@webpay_rails_params.except(:private_key)) }.to raise_error(WebpayRails::MissingPrivateKey) } 13 | it { expect{ Order::Blank.webpay_rails(@webpay_rails_params.except(:webpay_cert)) }.to raise_error(WebpayRails::MissingWebpayCertificate) } 14 | it { expect{ Order::Blank.webpay_rails(@webpay_rails_params.except(:public_cert)) }.to raise_error(WebpayRails::MissingPublicCertificate) } 15 | it { expect{ Order::Blank.webpay_rails(@webpay_rails_params.merge(private_key: '', public_cert: '', webpay_cert: '')) }.to raise_error(WebpayRails::FileNotFound) } 16 | it { expect{ Order::Blank.webpay_rails(@webpay_rails_params.merge(environment: :other)) }.to raise_error(WebpayRails::InvalidEnvironment) } 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | 3 | Bundler.require :default, :development 4 | 5 | require 'capybara/rspec' 6 | 7 | Combustion.initialize! :active_record, :action_controller, :action_view 8 | 9 | require 'rspec/rails' 10 | require 'capybara/rails' 11 | 12 | Capybara.register_driver :chrome do |app| 13 | capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( 14 | chromeOptions: { args: %w[window-size=1024,768] } 15 | ) 16 | 17 | Capybara::Selenium::Driver.new app, browser: :chrome, 18 | desired_capabilities: capabilities 19 | end 20 | 21 | Capybara.register_driver :headless_chrome do |app| 22 | capabilities = Selenium::WebDriver::Remote::Capabilities.chrome( 23 | chromeOptions: { args: %w[headless disable-gpu window-size=1024,768] } 24 | ) 25 | 26 | Capybara::Selenium::Driver.new app, browser: :chrome, 27 | desired_capabilities: capabilities 28 | end 29 | 30 | driver_name = (ENV['CAPYBARA_DRIVER'] || 'headless_chrome').to_sym 31 | Capybara.javascript_driver = driver_name 32 | Capybara.default_driver = driver_name 33 | Capybara.default_max_wait_time = 10 34 | 35 | require 'coveralls' 36 | 37 | Coveralls.wear! 38 | 39 | require 'webpay_rails' 40 | 41 | RSpec.configure do |config| 42 | config.use_transactional_fixtures = true 43 | end 44 | -------------------------------------------------------------------------------- /spec/internal/vendor/vault/597020000541.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDujCCAqICCQCZ42cY33KRTzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UEBhMC 3 | Q0wxETAPBgNVBAgMCFNhbnRpYWdvMRIwEAYDVQQKDAlUcmFuc2JhbmsxETAPBgNV 4 | BAcMCFNhbnRpYWdvMRUwEwYDVQQDDAw1OTcwMjAwMDA1NDExFzAVBgNVBAsMDkNh 5 | bmFsZXNSZW1vdG9zMSUwIwYJKoZIhvcNAQkBFhZpbnRlZ3JhZG9yZXNAdmFyaW9z 6 | LmNsMB4XDTE2MDYyMjIxMDkyN1oXDTI0MDYyMDIxMDkyN1owgZ4xCzAJBgNVBAYT 7 | AkNMMREwDwYDVQQIDAhTYW50aWFnbzESMBAGA1UECgwJVHJhbnNiYW5rMREwDwYD 8 | VQQHDAhTYW50aWFnbzEVMBMGA1UEAwwMNTk3MDIwMDAwNTQxMRcwFQYDVQQLDA5D 9 | YW5hbGVzUmVtb3RvczElMCMGCSqGSIb3DQEJARYWaW50ZWdyYWRvcmVzQHZhcmlv 10 | cy5jbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANApVXB/EQtbviqQ 11 | j1J82EiHJslyPO0RLAZEM2gNUCXuMPwe4OirylfQ2qn5zYTL3ff8qZhn9EQ0D4ZQ 12 | Wxpc8pis9cYdJUAQ/vTGa4PCbvP3dZNKSG9UC0A54UYEdk9eJ4F6T+DyECrauw7H 13 | RwcmxVOb7wClanR7yJmRc6nsW2Y11scYU/v7BnA+FbOu933Ogfl49lKyhe0MleaT 14 | A6tBwgi0zJmn1gJjax8peopkDPm6gtdoJxBABJKyYkjuj/9tZvYgybOSkGp6SW8t 15 | GhlvJqHGERwzB2Y7U4iD9PiQCYC/SrB/q13ZNm0+2nq3u08ziv2xMIZGBF5zDLf+ 16 | ub1egH8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdgNpIS2NZFx5PoYwJZf8faze 17 | NmKQg73seDGuP8d8w/CZf1Py/gsJFNbh4CEySWZRCzlOKxzmtPTmyPdyhObjMA8E 18 | Adps9DtgiN2ITSF1HUFmhMjI5V7U2L9LyEdpUaieYyPBfxiicdWz2YULVuOYDJHR 19 | n05jlj/EjYa5bLKs/yggYiqMkZdIX8NiLL6ZTERIvBa6azDKs6yDsCsnE1M5tzQI 20 | VVEkZtEfil6E1tz8v3yLZapLt+8jmPq1RCSx3Zh4fUkxBTpUW/9SWUNEXbKK7bB3 21 | zfB3kGE55K5nxHKfQlrqdHLcIo+vdShATwYnmhUkGxUnM9qoCDlB8lYu3rFi9w== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /spec/internal/vendor/vault/597020000547.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDujCCAqICCQCFNCTEl24W2TANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UEBhMC 3 | Q0wxETAPBgNVBAgMCFNhbnRpYWdvMRIwEAYDVQQKDAlUcmFuc2JhbmsxETAPBgNV 4 | BAcMCFNhbnRpYWdvMRUwEwYDVQQDDAw1OTcwMjAwMDA1NDcxFzAVBgNVBAsMDkNh 5 | bmFsZXNSZW1vdG9zMSUwIwYJKoZIhvcNAQkBFhZpbnRlZ3JhZG9yZXNAdmFyaW9z 6 | LmNsMB4XDTE2MDYyMzE2MzcxM1oXDTI0MDYyMTE2MzcxM1owgZ4xCzAJBgNVBAYT 7 | AkNMMREwDwYDVQQIDAhTYW50aWFnbzESMBAGA1UECgwJVHJhbnNiYW5rMREwDwYD 8 | VQQHDAhTYW50aWFnbzEVMBMGA1UEAwwMNTk3MDIwMDAwNTQ3MRcwFQYDVQQLDA5D 9 | YW5hbGVzUmVtb3RvczElMCMGCSqGSIb3DQEJARYWaW50ZWdyYWRvcmVzQHZhcmlv 10 | cy5jbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAORastwv3YYHVbBp 11 | L+aeRi73fTsXjr/qaCuNc08SFFzi6RujRFvh2+UM9K05NUxFSVAklW9G1fJ37A9K 12 | B6JQ/+yFP877xFifMyOXGu1Wdrmduy7nngGrM0X0RlpCfE6kQfKhP0LhyFFSRIGP 13 | WMaRJg9uYRCJWVPeBUUUXB0qSdfvKNyvQ0qpG3vWlhhr7Pocc65U4TuPBwFMC9Bd 14 | qMjKsrEkIUamX33kmTKeKrCpRO4iK+aZPN+90Ki6328Jtn3OevkRtIFUOO0ik36T 15 | 8dkVTPZ2ydwKGLaaBHOB0WlJ1QieWIhmMuvQrFDST7I/WUy9Wr0ySul58oKFT6Jf 16 | Xe19dysCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAubHNHDzyMLXuNQwawdhrzJYf 17 | Cvi2NsAqVBKICp+VC94OqVsdWknrqm8+Wz+1DZV1ezTvoVgagiC/ZrfHvn9DEP45 18 | 7JttrOt2Sbr+F2Pj3oBl1RiQ2QkIXBRaSmipKaQB/cWRd0ZiO7uT5mP7eQtO5qFJ 19 | 4WST6dXtks2Oz4G7eMpqnctOfFiGBi1i6omD7LZg+qpbeTFWTEgZFcAUTrViRLl2 20 | PEhUMVAobvvY7zUmzeu2mAMlWVNoaJysl6sH7Gii3T/xbxHsbxV8bZgvgQwiwFVP 21 | +ffp06jqVndIhoeiTOz0MXgPIXIESaDraY2dgNTgEs2GwLNjy2cMB5pkjkAZ4g== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /spec/nullify_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'vault_helper' 3 | 4 | describe WebpayRails::Responses::TransactionNullify do 5 | before(:all) { @approved_order = Order::Normal.normal_selling.approved.first! } 6 | before(:all) do 7 | @nullify_params = 8 | { 9 | authorization_code: @approved_order.tbk_authorization_code, 10 | authorized_amount: @approved_order.amount, 11 | buy_order: @approved_order.tbk_buy_order, 12 | nullify_amount: @approved_order.amount 13 | } 14 | end 15 | 16 | context 'when all is ok' do 17 | before(:all) { @nullified_transaction = Order::Normal.nullify(@nullify_params) } 18 | 19 | it { expect(@nullified_transaction).to be_kind_of(WebpayRails::Responses::TransactionNullify) } 20 | 21 | describe '.token' do 22 | it { expect(@nullified_transaction.token).not_to be_blank } 23 | end 24 | 25 | describe '.authorization_code' do 26 | it { expect(@nullified_transaction.authorization_code).not_to be_blank } 27 | end 28 | 29 | describe '.authorization_date' do 30 | it { expect(@nullified_transaction.authorization_date).not_to be_blank } 31 | end 32 | 33 | describe '.balance' do 34 | it { expect(@nullified_transaction.balance).not_to be_blank } 35 | end 36 | 37 | describe '.nullified_amount' do 38 | it { expect(@nullified_transaction.nullified_amount).not_to be_blank } 39 | end 40 | end 41 | 42 | context 'when not' do 43 | it { expect { Order::Normal.nullify(@nullify_params.merge(nullify_amount: 0)) }.to raise_error(WebpayRails::RequestFailed) } 44 | pending 'should raise WebpayRails::InvalidCertificate' 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /webpay_rails.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path('../lib', __FILE__) 3 | 4 | require 'webpay_rails/version' 5 | 6 | Gem::Specification.new do |s| 7 | s.platform = Gem::Platform::RUBY 8 | s.name = 'webpay_rails' 9 | s.version = WebpayRails::VERSION 10 | s.summary = 'WebpayRails is an easy solution for integrate Transbank Webpay in Rails applications.' 11 | s.description = s.summary 12 | s.authors = ['Sebastián Orellana'] 13 | s.email = ['limcross@gmail.com'] 14 | s.homepage = 'https://github.com/limcross/webpay_rails' 15 | s.license = 'MIT' 16 | s.require_paths = ['lib'] 17 | s.files = `git ls-files`.split("\n") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | 20 | s.required_ruby_version = '>= 2.2.2' 21 | 22 | s.add_dependency 'signer', '~> 1.4.3' 23 | s.add_dependency 'savon', '~> 2' 24 | s.add_dependency 'nokogiri', '~> 1.6', '>= 1.6.7.2' 25 | s.add_dependency 'activesupport', '>= 4', '< 5.1' 26 | s.add_dependency 'railties', '>= 4.1.0', '< 5.1' 27 | 28 | s.add_development_dependency 'rspec', '~> 3.5' 29 | s.add_development_dependency 'rspec-rails', '~> 3.5' 30 | s.add_development_dependency 'capybara', '~> 2.10', '>= 2.10.1' 31 | s.add_development_dependency 'selenium-webdriver', '~> 3.0' 32 | s.add_development_dependency 'combustion', '~> 0.5.5' 33 | s.add_development_dependency 'activerecord', '~> 5.0', '>= 5.0.0.1' 34 | s.add_development_dependency 'actionpack', '~> 5.0', '>= 5.0.0.1' 35 | s.add_development_dependency 'sqlite3' 36 | s.add_development_dependency 'rake' 37 | s.add_development_dependency 'coveralls' 38 | end 39 | -------------------------------------------------------------------------------- /spec/internal/app/controllers/oneclick_orders_controller.rb: -------------------------------------------------------------------------------- 1 | class OneclickOrdersController < ActionController::Base 2 | def new 3 | @order = Order::Oneclick.new 4 | end 5 | 6 | def create 7 | @user = User.first! 8 | @order = Order::Oneclick.create!(create_params) 9 | @response = Order::Oneclick.authorize(authorize_params) 10 | if @response.success? 11 | @order.update!(update_params.merge(status: :approved)) 12 | render :success 13 | else 14 | render :failed 15 | end 16 | rescue WebpayRails::SoapError 17 | render :failed 18 | end 19 | 20 | private 21 | 22 | def create_params 23 | params.require(:order_oneclick).permit(:amount) 24 | end 25 | 26 | def authorize_params 27 | { 28 | buy_order: @order.buy_order_for_transbank_oneclick, 29 | tbk_user: @user.tbk_user, 30 | username: @user.username, 31 | amount: @order.amount 32 | } 33 | end 34 | 35 | def update_params 36 | { 37 | #tbk_token_ws: '', 38 | #tbk_accounting_date: '', 39 | tbk_buy_order: @order.buy_order_for_transbank_oneclick, 40 | tbk_card_number: @response.last_4_card_digits, 41 | #tbk_commerce_code: '', 42 | tbk_authorization_code: @response.authorization_code, 43 | tbk_payment_type_code: 'VN', 44 | tbk_response_code: @response.response_code, 45 | #tbk_transaction_date: '', 46 | #tbk_vci: '', 47 | #tbk_session_id: '', 48 | #tbk_card_expiration_date: '', 49 | tbk_shares_number: 0, 50 | amount: @order.amount, 51 | 52 | tbk_transaction_id: @response.transaction_id, 53 | tbk_credit_card_type: @response.credit_card_type 54 | } 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/webpay_rails/soap_normal.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class SoapNormal < Soap 3 | def init_transaction(args) 4 | request = client.build_request(:init_transaction, 5 | message: init_transaction_message(args)) 6 | 7 | call(request, :init_transaction) 8 | end 9 | 10 | def get_transaction_result(args) 11 | request = client.build_request(:get_transaction_result, 12 | message: { tokenInput: args[:token] }) 13 | 14 | call(request, :get_transaction_result) 15 | end 16 | 17 | def acknowledge_transaction(args) 18 | request = client.build_request(:acknowledge_transaction, 19 | message: { tokenInput: args[:token] }) 20 | 21 | call(request, :acknowledge_transaction) 22 | end 23 | 24 | private 25 | 26 | def wsdl_path 27 | case @environment 28 | when :production 29 | 'https://webpay3g.transbank.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl' 30 | when :certification, :integration 31 | 'https://webpay3gint.transbank.cl/WSWebpayTransaction/cxf/WSWebpayService?wsdl' 32 | end 33 | end 34 | 35 | def init_transaction_message(args) 36 | { 37 | wsInitTransactionInput: { 38 | wSTransactionType: 'TR_NORMAL_WS', 39 | buyOrder: args[:buy_order], 40 | sessionId: args[:session_id], 41 | returnURL: args[:return_url], 42 | finalURL: args[:final_url], 43 | transactionDetails: { 44 | amount: args[:amount], 45 | commerceCode: @commerce_code, 46 | buyOrder: args[:buy_order] 47 | } 48 | } 49 | } 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/internal/vendor/vault/597020000541.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA0ClVcH8RC1u+KpCPUnzYSIcmyXI87REsBkQzaA1QJe4w/B7g 3 | 6KvKV9DaqfnNhMvd9/ypmGf0RDQPhlBbGlzymKz1xh0lQBD+9MZrg8Ju8/d1k0pI 4 | b1QLQDnhRgR2T14ngXpP4PIQKtq7DsdHBybFU5vvAKVqdHvImZFzqexbZjXWxxhT 5 | +/sGcD4Vs673fc6B+Xj2UrKF7QyV5pMDq0HCCLTMmafWAmNrHyl6imQM+bqC12gn 6 | EEAEkrJiSO6P/21m9iDJs5KQanpJby0aGW8mocYRHDMHZjtTiIP0+JAJgL9KsH+r 7 | Xdk2bT7aere7TzOK/bEwhkYEXnMMt/65vV6AfwIDAQABAoIBAHnIlOn6DTi99eXl 8 | KVSzIb5dA747jZWMxFruL70ifM+UKSh30FGPoBP8ZtGnCiw1ManSMk6uEuSMKMEF 9 | 5iboVi4okqnTh2WSC/ec1m4BpPQqxKjlfrdTTjnHIxrZpXYNucMwkeci93569ZFR 10 | 2SY/8pZV1mBkZoG7ocLmq+qwE1EaBEL/sXMvuF/h08nJ71I4zcclpB8kN0yFrBCW 11 | 7scqOwTLiob2mmU2bFHOyyjTkGOlEsBQxhtVwVEt/0AFH/ucmMTP0vrKOA0HkhxM 12 | oeR4k2z0qwTzZKXuEZtsau8a/9B3S3YcgoSOhRP/VdY1WL5hWDHeK8q1Nfq2eETX 13 | jnQ4zjECgYEA7z2/biWe9nDyYDZM7SfHy1xF5Q3ocmv14NhTbt8iDlz2LsZ2JcPn 14 | EMV++m88F3PYdFUOp4Zuw+eLJSrBqfuPYrTVNH0v/HdTqTS70R2YZCFb9g0ryaHV 15 | TRwYovu/oQMV4LBSzrwdtCrcfUZDtqMYmmZfEkdjCWCEpEi36nlG0JMCgYEA3r49 16 | o+soFIpDqLMei1tF+Ah/rm8oY5f4Wc82kmSgoPFCWnQEIW36i/GRaoQYsBp4loue 17 | vyPuW+BzoZpVcJDuBmHY3UOLKr4ZldOn2KIj6sCQZ1mNKo5WuZ4YFeL5uyp9Hvio 18 | TCPGeXghG0uIk4emSwolJVSbKSRi6SPsiANff+UCgYEAvNMRmlAbLQtsYb+565xw 19 | NvO3PthBVL4dLL/Q6js21/tLWxPNAHWklDosxGCzHxeSCg9wJ40VM4425rjebdld 20 | DF0Jwgnkq/FKmMxESQKA2tbxjDxNCTGv9tJsJ4dnch/LTrIcSYt0LlV9/WpN24LS 21 | 0lpmQzkQ07/YMQosDuZ1m/0CgYEAu9oHlEHTmJcO/qypmu/ML6XDQPKARpY5Hkzy 22 | gj4ZdgJianSjsynUfsepUwK663I3twdjR2JfON8vxd+qJPgltf45bknziYWvgDtz 23 | t/Duh6IFZxQQSQ6oN30MZRD6eo4X3dHp5eTaE0Fr8mAefAWQCoMw1q3m+ai1PlhM 24 | uFzX4r0CgYEArx4TAq+Z4crVCdABBzAZ7GvvAXdxvBo0AhD9IddSWVTCza972wta 25 | 5J2rrS/ye9Tfu5j2IbTHaLDz14mwMXr1S4L39UX/NifLc93KHie/yjycCuu4uqNo 26 | MtdweTnQt73lN2cnYedRUhw9UTfPzYu7jdXCUAyAD4IEjFQrswk2x04= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /spec/internal/vendor/vault/597020000547.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA5Fqy3C/dhgdVsGkv5p5GLvd9OxeOv+poK41zTxIUXOLpG6NE 3 | W+Hb5Qz0rTk1TEVJUCSVb0bV8nfsD0oHolD/7IU/zvvEWJ8zI5ca7VZ2uZ27Luee 4 | AaszRfRGWkJ8TqRB8qE/QuHIUVJEgY9YxpEmD25hEIlZU94FRRRcHSpJ1+8o3K9D 5 | Sqkbe9aWGGvs+hxzrlThO48HAUwL0F2oyMqysSQhRqZffeSZMp4qsKlE7iIr5pk8 6 | 373QqLrfbwm2fc56+RG0gVQ47SKTfpPx2RVM9nbJ3AoYtpoEc4HRaUnVCJ5YiGYy 7 | 69CsUNJPsj9ZTL1avTJK6XnygoVPol9d7X13KwIDAQABAoIBAQDPB6/rUvYbIqE8 8 | nFESW+KziCwgm/4O3x1shwTI5lJR2GORbBd42i973aAjQJ+is5qBL3nP9j/YYYNC 9 | ZVLAhYFR1YkBRl9AHa3GkaOXE/H13RwsrU8ioi2NOadjA64humgT6r8pCvyLRfPY 10 | JrdM56HDEcassGmtULgkZg4RXxqtym6dcpPmuXtCHFZ7JEWVSKWH+STReAh7unwD 11 | TYOZlsnU5FuSzYlN2IC0OtZORviTSiEkI1XMyrDm97HQWOCkwa6OizKf/xFdvX5X 12 | xcksALubSX1I+2sUHqP0LbZ3rkxgG6VoaJGF3zZdsjUPfFHBy/IGnbpcMqe/YkZl 13 | AduyI4KhAoGBAP5iLhQr9k/vKUXNgl/9H5wMWBqf3WvOgH4WiApUZ5i5e5JkOgav 14 | 6LSsGdLYhEADYAKYgRUOpwDE6IBcWobTCwe6O0R7Us9r/c1ex3zEPcqiMqULyAxx 15 | LLu34O75t7ctPvhewKfNs16qzl751ZWYInBbQoZf8CkZMDSxsQGCbTIRAoGBAOXO 16 | LOutW0anleTsBbQwbR1k0hIPk6CwQnutq1BcsELYBSNKh6DIA16zBqyE9oFDsS34 17 | VLvXETZZKGEUoPju28DVH+Scic5E15CPNUaJie+Ief97fA/iPLJNJFSpcsJG/+Rd 18 | jB1hurkcvjzp67wNk9z+WJBxcMfAo/KwSxbkNtl7AoGBAKUitycBIvThHLnjny8Q 19 | 8uQqX0dpYCQL+f3gQo/yGw5Z2o494i1VJIuk7V6ij7e+eSU2OxWgXWlyajxpt5qu 20 | hgqOKstaA3gDcs9PJ9Em07YndRkPfN4W2iNCSxLXqRuQk8BIQmiscDSUTUP6i1yB 21 | Vln55EW3IgCMCW8rqux/7sMBAoGAHkrqUvrcIFkxAic2rUUA7TIAGw9gl3sEmIcR 22 | IRvGxFjzfG5zqHcVMqOIyq8QS4Pf1D569PPpue9QylNM0OOzphyyApG7/KvIeq7W 23 | CAFTZHbqFgpyFSnudFaE5oAbt45iZvkJ4kmisooebassPvLPPf9tL0U056/2LKSe 24 | kVrt/AcCgYAWmgeAtQ4H6mX3yPeEa4uDJevjaVKxlKYUPdd8kCD3OTjX9/Pig2GH 25 | XhI5UeVd2k4EBdrY1DRVr+cJ8/fEKzcjOKyrbs0XTld8XCBeVuQKAliYS7PpG70C 26 | 3/jXM8HZyqbPB+apW35Ucqo84ClgPN8LUOi/tE/aP11awxmgTk9F+w== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /lib/webpay_rails/errors.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | # Generic WebpayRails exception class. 3 | class WebpayRailsError < StandardError; end 4 | 5 | # Raise when the commerce code has not been defined. 6 | class MissingCommerceCode < WebpayRailsError; end 7 | # Raise when the environment is not valid. 8 | class InvalidEnvironment < WebpayRailsError; end 9 | 10 | # Generic Soap exception class. 11 | class SoapError < WebpayRailsError 12 | def initialize(action, error) 13 | super("Attempted to #{action} but #{error}") 14 | end 15 | end 16 | 17 | # Raise when the SOAP request has failed. 18 | class RequestFailed < SoapError 19 | def initialize(action, error) 20 | super(action, "SOAP responds with a #{error.http.code} " \ 21 | "status code: #{error}") 22 | end 23 | end 24 | # Raise when the SOAP response of a request is blank. 25 | class InvalidResponse < SoapError 26 | def initialize(action) 27 | super(action, 'SOAP response is blank') 28 | end 29 | end 30 | # Raise when the SOAP response cannot be verify with the webpay cert. 31 | class InvalidCertificate < SoapError 32 | def initialize(action) 33 | super(action, 'the response was not signed with the correct certificate') 34 | end 35 | end 36 | 37 | # Generic Vault exception class. 38 | class VaultError < WebpayRailsError; end 39 | 40 | # Raise when vault cant load the file. 41 | class FileNotFound < VaultError; end 42 | # Raise when private key has not been defined. 43 | class MissingPrivateKey < VaultError; end 44 | # Raise when public certificate has not been defined. 45 | class MissingPublicCertificate < VaultError; end 46 | # Raise when webpay certificate has not been defined. 47 | class MissingWebpayCertificate < VaultError; end 48 | end 49 | -------------------------------------------------------------------------------- /lib/webpay_rails/verifier.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class Verifier 3 | def self.verify(document, cert) 4 | document = Nokogiri::XML(document.to_s, &:noblanks) 5 | signed_info_node = document.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo', { 6 | ds: 'http://www.w3.org/2000/09/xmldsig#', 7 | wsse: 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', 8 | soap: 'http://schemas.xmlsoap.org/soap/envelope/' 9 | }) 10 | 11 | # check that digest match 12 | check_digest(document, signed_info_node) && check_signature(document, signed_info_node, cert) 13 | end 14 | 15 | def self.check_digest(doc, signed_info_node) 16 | signed_info_node.xpath("//ds:Reference", ds: 'http://www.w3.org/2000/09/xmldsig#').each do |node| 17 | return false if !process_ref_node(doc, node) 18 | end 19 | 20 | true 21 | end 22 | 23 | def self.check_signature(doc, signed_info_node, cert) 24 | signed_info_canon = canonicalize(signed_info_node, ['soap']) 25 | signature = doc.at_xpath('//wsse:Security/ds:Signature/ds:SignatureValue', { 26 | ds: 'http://www.w3.org/2000/09/xmldsig#', 27 | wsse: 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' 28 | }).text 29 | 30 | cert.public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(signature), signed_info_canon) 31 | end 32 | 33 | def self.digest(message) 34 | OpenSSL::Digest::SHA1.new.reset.digest(message) 35 | end 36 | 37 | def self.process_ref_node(doc, node) 38 | uri = node.attr('URI') 39 | element = doc.at_xpath("//*[@wsu:Id='#{uri[1..-1]}']", wsu: 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd') 40 | target = canonicalize(element, nil) 41 | my_digest_value = Base64.encode64(digest(target)).strip 42 | digest_value = node.at_xpath('//ds:DigestValue', ds: 'http://www.w3.org/2000/09/xmldsig#').text 43 | 44 | my_digest_value == digest_value 45 | end 46 | 47 | def self.canonicalize(node = document, inclusive_namespaces=nil) 48 | # The last argument should be exactly +nil+ to remove comments from result 49 | node.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, inclusive_namespaces, nil) 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/webpay_rails/soap_oneclick.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class SoapOneclick < Soap 3 | def init_inscription(args) 4 | request = client.build_request(:init_inscription, 5 | message: init_inscription_message(args)) 6 | 7 | call(request, :init_inscription) 8 | end 9 | 10 | def finish_inscription(args) 11 | request = client.build_request(:finish_inscription, 12 | message: finish_inscription_message(args)) 13 | 14 | call(request, :finish_inscription) 15 | end 16 | 17 | def authorize(args) 18 | request = client.build_request(:authorize, 19 | message: authorize_message(args)) 20 | 21 | call(request, :authorize) 22 | end 23 | 24 | def reverse(args) 25 | request = client.build_request(:reverse, 26 | message: reverse_message(args)) 27 | 28 | call(request, :reverse) 29 | end 30 | 31 | def remove_user(args) 32 | request = client.build_request(:remove_user, 33 | message: remove_user_message(args)) 34 | 35 | call(request, :remove_user) 36 | end 37 | 38 | private 39 | 40 | def wsdl_path 41 | case @environment 42 | when :production 43 | 'https://webpay3g.transbank.cl/webpayserver/wswebpay/OneClickPaymentService?wsdl' 44 | when :certification, :integration 45 | 'https://webpay3gint.transbank.cl/webpayserver/wswebpay/OneClickPaymentService?wsdl' 46 | end 47 | end 48 | 49 | def init_inscription_message(args) 50 | { 51 | arg0: { 52 | email: args[:email], 53 | responseURL: args[:return_url], 54 | username: args[:username] 55 | } 56 | } 57 | end 58 | 59 | def finish_inscription_message(args) 60 | { 61 | arg0: { 62 | token: args[:token] 63 | } 64 | } 65 | end 66 | 67 | def authorize_message(args) 68 | { 69 | arg0: { 70 | buyOrder: args[:buy_order], 71 | tbkUser: args[:tbk_user], 72 | username: args[:username], 73 | amount: args[:amount] 74 | } 75 | } 76 | end 77 | 78 | def reverse_message(args) 79 | { 80 | arg0: { 81 | buyorder: args[:buy_order] 82 | } 83 | } 84 | end 85 | 86 | def remove_user_message(args) 87 | { 88 | arg0: { 89 | tbkUser: args[:tbk_user], 90 | username: args[:username] 91 | } 92 | } 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/webpay_rails/soap.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | class Soap 3 | extend Savon::Model 4 | 5 | def initialize(args) 6 | @vault = args[:vault] 7 | @environment = args[:environment] || :integration 8 | @commerce_code = args[:commerce_code] 9 | 10 | raise WebpayRails::MissingCommerceCode unless @commerce_code 11 | 12 | unless valid_environments.include? @environment 13 | raise WebpayRails::InvalidEnvironment 14 | end 15 | 16 | self.class.client(wsdl: wsdl_path, log: args[:log] || true, 17 | logger: file_logger.extend(rails_logger), 18 | open_timeout: 15, read_timeout: 15) 19 | end 20 | 21 | private 22 | 23 | def call(request, operation) 24 | signed_document = sign_xml(request) 25 | 26 | response = client.call(operation) do 27 | xml signed_document.to_xml(save_with: 0) 28 | end 29 | 30 | verify_response(response, operation) 31 | rescue Savon::SOAPFault => error 32 | raise WebpayRails::RequestFailed.new(operation, error) 33 | end 34 | 35 | def sign_xml(input_xml) 36 | document = Nokogiri::XML(input_xml.body) 37 | envelope = document.at_xpath('//env:Envelope') 38 | envelope.prepend_child('') 39 | xml = document.to_s 40 | 41 | signer = Signer.new(xml) 42 | 43 | signer.cert = @vault.public_cert 44 | signer.private_key = @vault.private_key 45 | 46 | signer.document.xpath('//soapenv:Body', { soapenv: 'http://schemas.xmlsoap.org/soap/envelope/' }).each do |node| 47 | signer.digest!(node) 48 | end 49 | 50 | signer.sign!(issuer_serial: true) 51 | signed_xml = signer.to_xml 52 | 53 | document = Nokogiri::XML(signed_xml) 54 | x509data = document.at_xpath('//*[local-name()=\'X509Data\']') 55 | new_data = x509data.clone 56 | new_data.set_attribute('xmlns:ds', 'http://www.w3.org/2000/09/xmldsig#') 57 | 58 | n = Nokogiri::XML::Node.new('wsse:SecurityTokenReference', document) 59 | n.add_child(new_data) 60 | x509data.add_next_sibling(n) 61 | 62 | document 63 | end 64 | 65 | def verify_response(response, operation) 66 | raise(WebpayRails::InvalidResponse, operation) if response.blank? 67 | 68 | if WebpayRails::Verifier.verify(response, @vault.webpay_cert) 69 | response 70 | else 71 | raise WebpayRails::InvalidCertificate, operation 72 | end 73 | end 74 | 75 | def valid_environments 76 | [:production, :certification, :integration] 77 | end 78 | 79 | def rails_logger 80 | ActiveSupport::Logger.broadcast(WebpayRails.rails_logger) 81 | end 82 | 83 | def file_logger 84 | Logger.new(Rails.root.join("log/webpay.log")) 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /spec/internal/app/controllers/normal_orders_controller.rb: -------------------------------------------------------------------------------- 1 | class NormalOrdersController < ActionController::Base 2 | before_action :find_order, only: [:return, :final] 3 | before_action :verify_order, only: :return 4 | 5 | def new 6 | @order = Order::Normal.new 7 | end 8 | 9 | def create 10 | @order = Order::Normal.new(create_params) 11 | 12 | if @order.save 13 | if init_transaction 14 | render :gateway 15 | else 16 | @order.update(status: :failed) 17 | render :failed 18 | end 19 | else 20 | render action: :new 21 | end 22 | end 23 | 24 | def return 25 | if transaction_result && @order.update(update_params) && @result.approved? 26 | @method = :get 27 | @url = @result.url_redirection 28 | @token = params[:token_ws] 29 | @order.update(status: :approved) 30 | render :gateway 31 | else 32 | @order.update(status: :failed) 33 | render :failed 34 | end 35 | end 36 | 37 | def final 38 | if @order.approved? 39 | render :success 40 | else 41 | render :failed 42 | end 43 | end 44 | 45 | private 46 | 47 | def find_order 48 | @order = Order::Normal.find(params[:id]) 49 | end 50 | 51 | def create_params 52 | params.require(:order_normal).permit(:amount) 53 | end 54 | 55 | def update_params 56 | { 57 | tbk_token_ws: params[:token_ws], 58 | tbk_accounting_date: @result.accounting_date, 59 | tbk_buy_order: @result.buy_order, 60 | tbk_card_number: @result.card_number, 61 | tbk_commerce_code: @result.commerce_code, 62 | tbk_authorization_code: @result.authorization_code, 63 | tbk_payment_type_code: @result.payment_type_code, 64 | tbk_response_code: @result.response_code, 65 | tbk_transaction_date: @result.transaction_date, 66 | tbk_vci: @result.vci, 67 | tbk_session_id: @result.session_id, 68 | tbk_card_expiration_date: @result.card_expiration_date, 69 | tbk_shares_number: @result.shares_number, 70 | amount: @result.amount 71 | } 72 | end 73 | 74 | def verify_order 75 | render :failed if @order.approved? 76 | end 77 | 78 | def init_transaction 79 | transaction = Order::Normal.init_transaction(init_transaction_params) 80 | if transaction.success? 81 | @method = :post 82 | @url = transaction.url 83 | @token = transaction.token 84 | end 85 | 86 | transaction.success? 87 | rescue WebpayRails::SoapError 88 | false 89 | end 90 | 91 | def transaction_result 92 | @result = Order::Normal.transaction_result(token: params[:token_ws]) 93 | true 94 | rescue WebpayRails::SoapError 95 | false 96 | end 97 | 98 | def init_transaction_params 99 | { 100 | amount: @order.amount, 101 | buy_order: @order.buy_order_for_transbank_normal, 102 | session_id: session.id, 103 | return_url: url_for(action: :return, id: @order.id), 104 | final_url: url_for(action: :final, id: @order.id) 105 | } 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /spec/oneclick_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'vault_helper' 3 | 4 | describe 'Inscription flow' do 5 | let(:email) { 'john.doe@mail.com' } 6 | let(:username) { 'john' } 7 | 8 | before(:each) { visit new_oneclick_inscription_path } 9 | 10 | context 'when it accepted' do 11 | it 'it is sent to the success page' do 12 | # Rails app 13 | fill_in(id: 'user_email', with: email) 14 | fill_in(id: 'user_username', with: username) 15 | find('input[type=submit]').click 16 | 17 | # Webpay 18 | fill_in('TBK_NUMERO_TARJETA', with: '4051885600446623') 19 | fill_in('TBK_CVV', with: '123') 20 | click_button('button') 21 | 22 | # Demo Bank 23 | within_frame('transicion') do 24 | fill_in('rutClient', with: '111111111') 25 | fill_in('passwordClient', with: '123') 26 | find('input[type=submit]').click 27 | # It is not necessary because it is already selected by default 28 | # select('Aceptar', from: 'vci') 29 | find('input[type=submit]').click 30 | end 31 | 32 | # Webpay 33 | accept_alert if page.driver.options[:browser] == :firefox 34 | 35 | # Rails app 36 | expect(page).to have_content('Success inscription') 37 | end 38 | end 39 | 40 | context 'when it rejected' do 41 | it 'it is sent to the failure page' do 42 | # Rails app 43 | fill_in(id: 'user_email', with: email) 44 | fill_in(id: 'user_username', with: username) 45 | find('input[type=submit]').click 46 | 47 | # Webpay 48 | fill_in('TBK_NUMERO_TARJETA', with: '4051885600446623') 49 | fill_in('TBK_CVV', with: '123') 50 | click_button('button') 51 | 52 | # Demo Bank 53 | within_frame('transicion') do 54 | fill_in('rutClient', with: '111111111') 55 | fill_in('passwordClient', with: '123') 56 | find('input[type=submit]').click 57 | 58 | select('Rechazar', from: 'vci') 59 | find('input[type=submit]').click 60 | end 61 | 62 | # Webpay 63 | accept_alert if page.driver.options[:browser] == :firefox 64 | 65 | # Rails app 66 | expect(page).to have_content('Failed inscription') 67 | end 68 | end 69 | end 70 | 71 | describe 'Authorization flow' do 72 | before(:each) { visit new_oneclick_order_path } 73 | 74 | context 'when it accepted' do 75 | it 'it is sent to the success page' do 76 | # Rails app 77 | fill_in(id: 'order_oneclick_amount', with: '1000') 78 | find('input[type=submit]').click 79 | 80 | # Webpay 81 | accept_alert if page.driver.options[:browser] == :firefox 82 | 83 | # Rails app 84 | expect(page).to have_content('Success transaction') 85 | end 86 | end 87 | end 88 | 89 | describe 'Reverse order' do 90 | before(:all) { @buy_order = Order::Oneclick.approved.last!.buy_order_for_transbank_oneclick } 91 | 92 | context 'when all is ok' do 93 | before(:all) { @result = Order::Oneclick.reverse(buy_order: @buy_order) } 94 | 95 | it { expect(@result).to be_kind_of(WebpayRails::Responses::Reverse) } 96 | it { expect(@result.success?).to be_truthy } 97 | 98 | describe '.return' do 99 | it { expect(@result.return).not_to be_blank } 100 | end 101 | end 102 | 103 | context 'when not' do 104 | it { expect { Order::Oneclick.reverse(buy_order: '') }.to raise_error(WebpayRails::RequestFailed) } 105 | pending 'should raise WebpayRails::InvalidCertificate' 106 | end 107 | end 108 | 109 | describe 'Unsubscribe' do 110 | before(:all) { @tbk_user = User.first!.tbk_user } 111 | before(:all) { @username = 'john' } 112 | 113 | context 'when all is ok' do 114 | before(:all) { @result = Order::Oneclick.remove_user(tbk_user: @tbk_user, username: @username) } 115 | 116 | it { expect(@result).to be_kind_of(WebpayRails::Responses::RemoveUser) } 117 | it { expect(@result.success?).to be_truthy } 118 | 119 | describe '.return' do 120 | it { expect(@result.return).not_to be_blank } 121 | end 122 | end 123 | 124 | context 'when not' do 125 | it { expect { Order::Oneclick.remove_user(tbk_user: '', username: @username) }.to raise_error(WebpayRails::RequestFailed) } 126 | pending 'should raise WebpayRails::InvalidCertificate' 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /spec/vault_helper.rb: -------------------------------------------------------------------------------- 1 | COMMERCE_CODE = 597020000541 2 | PRIVATE_KEY = '-----BEGIN RSA PRIVATE KEY----- 3 | MIIEpQIBAAKCAQEA0ClVcH8RC1u+KpCPUnzYSIcmyXI87REsBkQzaA1QJe4w/B7g 4 | 6KvKV9DaqfnNhMvd9/ypmGf0RDQPhlBbGlzymKz1xh0lQBD+9MZrg8Ju8/d1k0pI 5 | b1QLQDnhRgR2T14ngXpP4PIQKtq7DsdHBybFU5vvAKVqdHvImZFzqexbZjXWxxhT 6 | +/sGcD4Vs673fc6B+Xj2UrKF7QyV5pMDq0HCCLTMmafWAmNrHyl6imQM+bqC12gn 7 | EEAEkrJiSO6P/21m9iDJs5KQanpJby0aGW8mocYRHDMHZjtTiIP0+JAJgL9KsH+r 8 | Xdk2bT7aere7TzOK/bEwhkYEXnMMt/65vV6AfwIDAQABAoIBAHnIlOn6DTi99eXl 9 | KVSzIb5dA747jZWMxFruL70ifM+UKSh30FGPoBP8ZtGnCiw1ManSMk6uEuSMKMEF 10 | 5iboVi4okqnTh2WSC/ec1m4BpPQqxKjlfrdTTjnHIxrZpXYNucMwkeci93569ZFR 11 | 2SY/8pZV1mBkZoG7ocLmq+qwE1EaBEL/sXMvuF/h08nJ71I4zcclpB8kN0yFrBCW 12 | 7scqOwTLiob2mmU2bFHOyyjTkGOlEsBQxhtVwVEt/0AFH/ucmMTP0vrKOA0HkhxM 13 | oeR4k2z0qwTzZKXuEZtsau8a/9B3S3YcgoSOhRP/VdY1WL5hWDHeK8q1Nfq2eETX 14 | jnQ4zjECgYEA7z2/biWe9nDyYDZM7SfHy1xF5Q3ocmv14NhTbt8iDlz2LsZ2JcPn 15 | EMV++m88F3PYdFUOp4Zuw+eLJSrBqfuPYrTVNH0v/HdTqTS70R2YZCFb9g0ryaHV 16 | TRwYovu/oQMV4LBSzrwdtCrcfUZDtqMYmmZfEkdjCWCEpEi36nlG0JMCgYEA3r49 17 | o+soFIpDqLMei1tF+Ah/rm8oY5f4Wc82kmSgoPFCWnQEIW36i/GRaoQYsBp4loue 18 | vyPuW+BzoZpVcJDuBmHY3UOLKr4ZldOn2KIj6sCQZ1mNKo5WuZ4YFeL5uyp9Hvio 19 | TCPGeXghG0uIk4emSwolJVSbKSRi6SPsiANff+UCgYEAvNMRmlAbLQtsYb+565xw 20 | NvO3PthBVL4dLL/Q6js21/tLWxPNAHWklDosxGCzHxeSCg9wJ40VM4425rjebdld 21 | DF0Jwgnkq/FKmMxESQKA2tbxjDxNCTGv9tJsJ4dnch/LTrIcSYt0LlV9/WpN24LS 22 | 0lpmQzkQ07/YMQosDuZ1m/0CgYEAu9oHlEHTmJcO/qypmu/ML6XDQPKARpY5Hkzy 23 | gj4ZdgJianSjsynUfsepUwK663I3twdjR2JfON8vxd+qJPgltf45bknziYWvgDtz 24 | t/Duh6IFZxQQSQ6oN30MZRD6eo4X3dHp5eTaE0Fr8mAefAWQCoMw1q3m+ai1PlhM 25 | uFzX4r0CgYEArx4TAq+Z4crVCdABBzAZ7GvvAXdxvBo0AhD9IddSWVTCza972wta 26 | 5J2rrS/ye9Tfu5j2IbTHaLDz14mwMXr1S4L39UX/NifLc93KHie/yjycCuu4uqNo 27 | MtdweTnQt73lN2cnYedRUhw9UTfPzYu7jdXCUAyAD4IEjFQrswk2x04= 28 | -----END RSA PRIVATE KEY-----'.freeze 29 | PUBLIC_CERT = '-----BEGIN CERTIFICATE----- 30 | MIIDujCCAqICCQCZ42cY33KRTzANBgkqhkiG9w0BAQsFADCBnjELMAkGA1UEBhMC 31 | Q0wxETAPBgNVBAgMCFNhbnRpYWdvMRIwEAYDVQQKDAlUcmFuc2JhbmsxETAPBgNV 32 | BAcMCFNhbnRpYWdvMRUwEwYDVQQDDAw1OTcwMjAwMDA1NDExFzAVBgNVBAsMDkNh 33 | bmFsZXNSZW1vdG9zMSUwIwYJKoZIhvcNAQkBFhZpbnRlZ3JhZG9yZXNAdmFyaW9z 34 | LmNsMB4XDTE2MDYyMjIxMDkyN1oXDTI0MDYyMDIxMDkyN1owgZ4xCzAJBgNVBAYT 35 | AkNMMREwDwYDVQQIDAhTYW50aWFnbzESMBAGA1UECgwJVHJhbnNiYW5rMREwDwYD 36 | VQQHDAhTYW50aWFnbzEVMBMGA1UEAwwMNTk3MDIwMDAwNTQxMRcwFQYDVQQLDA5D 37 | YW5hbGVzUmVtb3RvczElMCMGCSqGSIb3DQEJARYWaW50ZWdyYWRvcmVzQHZhcmlv 38 | cy5jbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANApVXB/EQtbviqQ 39 | j1J82EiHJslyPO0RLAZEM2gNUCXuMPwe4OirylfQ2qn5zYTL3ff8qZhn9EQ0D4ZQ 40 | Wxpc8pis9cYdJUAQ/vTGa4PCbvP3dZNKSG9UC0A54UYEdk9eJ4F6T+DyECrauw7H 41 | RwcmxVOb7wClanR7yJmRc6nsW2Y11scYU/v7BnA+FbOu933Ogfl49lKyhe0MleaT 42 | A6tBwgi0zJmn1gJjax8peopkDPm6gtdoJxBABJKyYkjuj/9tZvYgybOSkGp6SW8t 43 | GhlvJqHGERwzB2Y7U4iD9PiQCYC/SrB/q13ZNm0+2nq3u08ziv2xMIZGBF5zDLf+ 44 | ub1egH8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdgNpIS2NZFx5PoYwJZf8faze 45 | NmKQg73seDGuP8d8w/CZf1Py/gsJFNbh4CEySWZRCzlOKxzmtPTmyPdyhObjMA8E 46 | Adps9DtgiN2ITSF1HUFmhMjI5V7U2L9LyEdpUaieYyPBfxiicdWz2YULVuOYDJHR 47 | n05jlj/EjYa5bLKs/yggYiqMkZdIX8NiLL6ZTERIvBa6azDKs6yDsCsnE1M5tzQI 48 | VVEkZtEfil6E1tz8v3yLZapLt+8jmPq1RCSx3Zh4fUkxBTpUW/9SWUNEXbKK7bB3 49 | zfB3kGE55K5nxHKfQlrqdHLcIo+vdShATwYnmhUkGxUnM9qoCDlB8lYu3rFi9w== 50 | -----END CERTIFICATE-----'.freeze 51 | WEBPAY_CERT = '-----BEGIN CERTIFICATE----- 52 | MIIDKTCCAhECBFZl7uIwDQYJKoZIhvcNAQEFBQAwWTELMAkGA1UEBhMCQ0wxDjAMBgNVBAgMBUNo 53 | aWxlMREwDwYDVQQHDAhTYW50aWFnbzEMMAoGA1UECgwDa2R1MQwwCgYDVQQLDANrZHUxCzAJBgNV 54 | BAMMAjEwMB4XDTE1MTIwNzIwNDEwNloXDTE4MDkwMjIwNDEwNlowWTELMAkGA1UEBhMCQ0wxDjAM 55 | BgNVBAgMBUNoaWxlMREwDwYDVQQHDAhTYW50aWFnbzEMMAoGA1UECgwDa2R1MQwwCgYDVQQLDANr 56 | ZHUxCzAJBgNVBAMMAjEwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAizJUWTDC7nfP 57 | 3jmZpWXFdG9oKyBrU0Bdl6fKif9a1GrwevThsU5Dq3wiRfYvomStNjFDYFXOs9pRIxqX2AWDybjA 58 | X/+bdDTVbM+xXllA9stJY8s7hxAvwwO7IEuOmYDpmLKP7J+4KkNH7yxsKZyLL9trG3iSjV6Y6SO5 59 | EEhUsdxoJFAow/h7qizJW0kOaWRcljf7kpqJAL3AadIuqV+hlf+Ts/64aMsfSJJA6xdbdp9ddgVF 60 | oqUl1M8vpmd4glxlSrYmEkbYwdI9uF2d6bAeaneBPJFZr6KQqlbbrVyeJZqmMlEPy0qPco1TIxrd 61 | EHlXgIFJLyyMRAyjX9i4l70xjwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBn3tUPS6e2USgMrPKp 62 | sxU4OTfW64+mfD6QrVeBOh81f6aGHa67sMJn8FE/cG6jrUmX/FP1/Cpbpvkm5UUlFKpgaFfHv+Kg 63 | CpEvgcRIv/OeIi6Jbuu3NrPdGPwzYkzlOQnmgio5RGb6GSs+OQ0mUWZ9J1+YtdZc+xTga0x7nsCT 64 | 5xNcUXsZKhyjoKhXtxJm3eyB3ysLNyuL/RHy/EyNEWiUhvt1SIePnW+Y4/cjQWYwNqSqMzTSW9TP 65 | 2QR2bX/W2H6ktRcLsgBK9mq7lE36p3q6c9DtZJE+xfA4NGCYWM9hd8pbusnoNO7AFxJZOuuvLZI7 66 | JvD7YLhPvCYKry7N6x3l 67 | -----END CERTIFICATE-----'.freeze 68 | -------------------------------------------------------------------------------- /lib/webpay_rails/base.rb: -------------------------------------------------------------------------------- 1 | module WebpayRails 2 | module Base 3 | extend ActiveSupport::Concern 4 | module ClassMethods 5 | # Setup a model for use Webpay Rails. 6 | # 7 | # ==== Variations of #webpay_rails 8 | # 9 | # # setup with certificates and private_key content 10 | # webpay_rails( 11 | # commerce_code: 123456789, 12 | # private_key: '-----BEGIN RSA PRIVATE KEY----- 13 | # ... 14 | # -----END RSA PRIVATE KEY-----', 15 | # public_cert: '-----BEGIN CERTIFICATE----- 16 | # ... 17 | # -----END CERTIFICATE-----', 18 | # webpay_cert: '-----BEGIN CERTIFICATE----- 19 | # ... 20 | # -----END CERTIFICATE-----', 21 | # environment: :integration, 22 | # log: true 23 | # ) 24 | # 25 | # # setup with certificates and private_key files 26 | # webpay_rails( 27 | # commerce_code: 123456789, 28 | # private_key: 'absolute/path/to/private_key.key', 29 | # public_cert: 'absolute/path/to/public_cert.crt', 30 | # webpay_cert: 'absolute/path/to/webpay_cert.crt', 31 | # environment: :integration, 32 | # log: true 33 | # ) 34 | def webpay_rails(args) 35 | class_attribute :vault, :soap_normal, :soap_nullify, :soap_oneclick, 36 | instance_accessor: false 37 | 38 | self.vault = args[:vault] = WebpayRails::Vault.new(args) 39 | self.soap_normal = WebpayRails::SoapNormal.new(args) 40 | self.soap_nullify = WebpayRails::SoapNullify.new(args) 41 | self.soap_oneclick = WebpayRails::SoapOneclick.new(args) 42 | end 43 | 44 | # Initializes a transaction 45 | # Returns a WebpayRails::Response::InitTransaction if successfully initialised. 46 | # If fault a WebpayRails::RequestFailed exception is raised. 47 | # If the SOAP response cant be verified a WebpayRails::InvalidCertificate 48 | # exception is raised. 49 | # 50 | # === Arguments 51 | # [:amount] 52 | # An integer that define the amount of the transaction. 53 | # [:buy_order] 54 | # An string that define the order number of the buy. 55 | # [:session_id] 56 | # An string that define a local variable that will be returned as 57 | # part of the result of the transaction. 58 | # [:return_url] 59 | # An string that define the url that Webpay redirect after client is 60 | # authorized (or not) by the bank for get the result of the transaction. 61 | # [:final_url] 62 | # An string that define the url that Webpay redirect after they show 63 | # the webpay invoice, or cancel the transaction from Webpay. 64 | def init_transaction(args) 65 | response = soap_normal.init_transaction(args) 66 | 67 | WebpayRails::Responses::InitTransaction.new(response) 68 | end 69 | 70 | # Retrieves the result of a transaction 71 | # Returns a WebpayRails::Response::TransactionResult if successfully get a response. 72 | # If fault a WebpayRails::RequestFailed exception is raised. 73 | # If the SOAP response cant be verified a WebpayRails::InvalidCertificate 74 | # exception is raised. 75 | # 76 | # === Arguments 77 | # [:token] 78 | # An string that responds Webpay when redirect to +return_url+. 79 | # [:ack] 80 | # An optional boolean with which you can disable the auto 81 | # acknowledgement (I guess if you do this, you will know what you do). 82 | def transaction_result(args) 83 | response = soap_normal.get_transaction_result(args) 84 | 85 | acknowledge_transaction(args) if args[:ack] != false 86 | 87 | WebpayRails::Responses::TransactionResult.new(response) 88 | end 89 | 90 | # Reports the correct reception of the result of the transaction 91 | # If fault a WebpayRails::RequestFailed exception is raised. 92 | # If the SOAP response cant be verified a WebpayRails::InvalidCertificate 93 | # exception is raised. 94 | # 95 | # === Arguments 96 | # [:token] 97 | # An string that responds Webpay when redirect to +return_url+. 98 | # 99 | # NOTE: It is not necessary to use this method because it is consumed by 100 | # +transaction_result+. 101 | def acknowledge_transaction(args) 102 | soap_normal.acknowledge_transaction(args) 103 | end 104 | 105 | # Nullify a transaction 106 | # Returns a WebpayRails::Response::TransactionNullify if successfully initialised. 107 | # If fault a WebpayRails::RequestFailed exception is raised. 108 | # If the SOAP response cant be verified a WebpayRails::InvalidCertificate 109 | # exception is raised. 110 | # 111 | # === Arguments 112 | # [:authorization_code] 113 | # An string that original belongs to the transaction. 114 | # [:authorize_amount] 115 | # An integer that define the original amount of the transaction. 116 | # [:buy_order] 117 | # An string that define the order number of the transaction to be 118 | # nullified. 119 | # [:nullify_amount] 120 | # An intenger that define the amount to be nullified on the transaction. 121 | def nullify(args) 122 | response = soap_nullify.nullify(args) 123 | 124 | WebpayRails::Responses::TransactionNullify.new(response) 125 | end 126 | 127 | def init_inscription(args) 128 | response = soap_oneclick.init_inscription(args) 129 | 130 | WebpayRails::Responses::InitInscription.new(response) 131 | end 132 | 133 | def finish_inscription(args) 134 | response = soap_oneclick.finish_inscription(args) 135 | 136 | WebpayRails::Responses::FinishInscription.new(response) 137 | end 138 | 139 | def authorize(args) 140 | response = soap_oneclick.authorize(args) 141 | 142 | WebpayRails::Responses::Authorization.new(response) 143 | end 144 | 145 | def reverse(args) 146 | response = soap_oneclick.reverse(args) 147 | 148 | WebpayRails::Responses::Reverse.new(response) 149 | end 150 | 151 | def remove_user(args) 152 | response = soap_oneclick.remove_user(args) 153 | 154 | WebpayRails::Responses::RemoveUser.new(response) 155 | end 156 | end 157 | end 158 | end 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebpayRails 2 | 3 | [![Build Status](https://travis-ci.org/limcross/webpay_rails.svg?branch=master)](https://travis-ci.org/limcross/webpay_rails) 4 | [![Code Climate](https://codeclimate.com/github/limcross/webpay_rails/badges/gpa.svg)](https://codeclimate.com/github/limcross/webpay_rails) 5 | [![Coverage Status](https://coveralls.io/repos/github/limcross/webpay_rails/badge.svg?branch=master)](https://coveralls.io/github/limcross/webpay_rails?branch=master) 6 | [![Dependency Status](https://gemnasium.com/badges/github.com/limcross/webpay_rails.svg)](https://gemnasium.com/github.com/limcross/webpay_rails) 7 | [![Gem Version](https://badge.fury.io/rb/webpay_rails.svg)](https://badge.fury.io/rb/webpay_rails) 8 | 9 | WebpayRails is an easy solution for integrate Transbank Webpay in Rails applications. 10 | 11 | _This gem (including certificates used in tests) was originally based on the SDK for Ruby (distributed under the **open source license**) available in www.transbankdevelopers.cl_ 12 | 13 | ### WebpayRails has been Deprecated 14 | 15 | __Transbank no longer supports SOAP integration. This repository has been deprecated and will no longer be maintained.__ 16 | 17 | You should consider using the official gem [transbank-sdk-ruby](https://github.com/TransbankDevelopers/transbank-sdk-ruby). 18 | 19 | ## Getting started 20 | 21 | __This README is only valid for the *master* branch, click [here](https://github.com/limcross/webpay_rails/blob/v1.1.1/README.md) for see the latest released version.__ 22 | 23 | You can add it to your `Gemfile`: 24 | 25 | ```ruby 26 | gem 'webpay_rails' 27 | ``` 28 | 29 | Run the bundle command to install it. 30 | 31 | ### Configuring models 32 | After that, extend the model to `WebpayRails` and add `webpay_rails` to this, like below. 33 | 34 | ```ruby 35 | class Order < ActiveRecord::Base 36 | extend WebpayRails 37 | 38 | webpay_rails( 39 | commerce_code: 123456789, 40 | private_key: '-----BEGIN RSA PRIVATE KEY----- 41 | ... 42 | -----END RSA PRIVATE KEY-----', 43 | public_cert: '-----BEGIN CERTIFICATE----- 44 | ... 45 | -----END CERTIFICATE-----', 46 | webpay_cert: '-----BEGIN CERTIFICATE----- 47 | ... 48 | -----END CERTIFICATE-----', 49 | environment: :integration, 50 | log: true 51 | ) 52 | end 53 | ``` 54 | 55 | As you can see for `private_key`, `public_cert`, and `webpay_cert`, the content of the files is entered, without prejudice to the above you can also indicate the absolute path to these files. 56 | 57 | The default `environment` is `:integration`, and the valid environments are `:integration`, `:certification` and `:production`. Depending on the environment is assigned the wsdl path. 58 | 59 | The `log` is very useful when generating the evidence of integration, and is enabled by default. The log can be found both in the rails log and in a separate file (`log\webpay.log`). 60 | 61 | Obviously all these values should not be defined directly in the model. It is strongly recommended to use environment variables for this ([dotenv](https://github.com/bkeepers/dotenv)). 62 | 63 | ### Using WebpayRails 64 | 65 | #### Initializing a transaction 66 | 67 | First we need to initialize an transaction, like below: 68 | 69 | ```ruby 70 | @transaction = Order.init_transaction(amount: amount, buy_order: buy_order, session_id: session_id, return_url: return_url, final_url: final_url) 71 | ``` 72 | 73 | Where `amount` is an __integer__ that define the amount of the transaction (_obviously_), `buy_order` is an __string__ that define the order number of the buy, `session_id` is an __string__ that define a local variable that will be returned as part of the result of the transaction, `return_url` and `final_url` are a __string__ for the redirections. 74 | 75 | This method return a `Transaction` object, that contain a redirection `url` and `token` for redirect the customer through POST method, like below. 76 | 77 | ```erb 78 | <% if @transaction.success? %> 79 | <%= form_tag(@transaction.url, method: :post, enforce_utf8: false, authenticity_token: false) do %> 80 | <%= hidden_field_tag(:token_ws, @transaction.token) %> 81 | <%= submit_tag("Pagar con Webpay") %> 82 | <% end %> 83 | <% end %> 84 | ``` 85 | 86 | Once Webpay displays the form of payment and authorization of the bank, the customer will send back through __POST__ method to the `return_url` with a `token_ws`. (If the customer cancels the transaction is directly returned to the `final_url` in the same way). 87 | 88 | #### Getting the result of a transaction 89 | 90 | When Webpay send a __POST__ to `return_url` with `token_ws`, we need to ask for the transaction result, like below. 91 | 92 | ```ruby 93 | @result = Order.transaction_result(token: params[:token_ws]) 94 | ``` 95 | 96 | This method return a `TransactionResult` object, that contain an `buy_order`, `session_id`, `accounting_date`, `transaction_date`, `vci`, `url_redirection`, `card_number`, `card_expiration_date`, `authorization_code`, `payment_type_code`, `response_code`, `amount`, `shares_number` and `commerce_code`. 97 | 98 | At this point we have confirmed the transaction with Transbank, performing the operation `acknowledge_transaction` by means of `transaction_result`. 99 | 100 | Now we need to send back the customer to `url_redirection` with `token_ws` through __GET__ method. 101 | 102 | #### Ending a transaction 103 | 104 | When Webpay send customer to `final_url`, we are done. Finally the transaction has ended. :clap: 105 | 106 | #### Nullify a transaction 107 | 108 | To cancel a transaction we must use the method described below. 109 | 110 | ```ruby 111 | @transaction = Order.nullify(authorization_code: authorization_code, authorized_amount: authorized_amount, buy_order: buy_order, nullify_amount: nullify_amount) 112 | ``` 113 | 114 | Where `authorization_code`, `authorized_amount`, `buy_order`, are known and corresponds to the transaction to be canceled, and `nullify_amount` it corresponds to the amount to be canceled. 115 | 116 | This method return a `TransactionNullified` object, that contain an `token`, `authorization_code`, `authorization_date`, `balance` and 117 | `nullified_amount`. 118 | 119 | Note that you can only make a single cancellation, whether total or partial. 120 | 121 | ## Contributing 122 | Any contribution is welcome. Personally I prefer to use English to do documentation and describe commits, however there is no problem if you make your comments and issues in Spanish. 123 | 124 | ### Reporting issues 125 | 126 | Please try to answer the following questions in your bug report: 127 | 128 | - What did you do? 129 | - What did you expect to happen? 130 | - What happened instead? 131 | 132 | Make sure to include as much relevant information as possible. Ruby version, 133 | WebpayRails version, OS version and any stack traces you have are very valuable. 134 | 135 | ### Pull Requests 136 | 137 | - __Add tests!__ Your patch won't be accepted if it doesn't have tests. 138 | 139 | - __Document any change in behaviour__. Make sure the README and any relevant documentation are kept up-to-date. 140 | 141 | - __Create topic branches__. Please don't ask us to pull from your master branch. 142 | 143 | - __One pull request per feature__. If you want to do more than one thing, send multiple pull requests. 144 | 145 | - __Send coherent history__. Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before sending them to us. 146 | -------------------------------------------------------------------------------- /spec/normal_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'vault_helper' 3 | 4 | describe WebpayRails::Responses::InitTransaction do 5 | let(:amount) { 1000 } 6 | let(:buy_order) { SecureRandom.uuid.first(30).delete!('-') } 7 | let(:session_id) { 'aj2h4kj2' } 8 | let(:return_url) { 'http://localhost:3000/tbknormal?option=return' } 9 | let(:final_url) { 'http://localhost:3000/tbknormal?option=final' } 10 | let(:init_transaction_params) do 11 | { amount: amount, 12 | buy_order: buy_order, 13 | session_id: session_id, 14 | return_url: return_url, 15 | final_url: final_url } 16 | end 17 | context 'when all is ok' do 18 | let!(:transaction) { Order::Normal.init_transaction(init_transaction_params) } 19 | 20 | it { expect(transaction).to be_kind_of(WebpayRails::Responses::InitTransaction) } 21 | 22 | describe '.token' do 23 | it { expect(transaction.token).not_to be_blank } 24 | end 25 | 26 | describe '.url' do 27 | it { expect(transaction.url).not_to be_blank } 28 | end 29 | 30 | describe '.success?' do 31 | it { expect(transaction.success?).to be_truthy } 32 | end 33 | end 34 | 35 | context 'when not' do 36 | it { expect { Order::Normal.init_transaction(init_transaction_params.merge(return_url: '', final_url: '')) }.to raise_error(WebpayRails::RequestFailed) } 37 | it { expect { Order::Normal.init_transaction(init_transaction_params.merge(amount: 0)) }.to raise_error(WebpayRails::RequestFailed) } 38 | it { expect { Order::NormalInvalid.init_transaction(init_transaction_params) }.to raise_error(WebpayRails::InvalidCertificate) } 39 | end 40 | end 41 | 42 | describe WebpayRails::Responses::TransactionResult do 43 | context 'when all is ok' do 44 | describe '.buy_order' do 45 | pending 'should not be blank' 46 | end 47 | 48 | describe '.session_id' do 49 | # pending 'should not be blank' NOTE: In fact it can 50 | end 51 | 52 | describe '.accounting_date' do 53 | pending 'should not be blank' 54 | end 55 | 56 | describe '.transaction_date' do 57 | pending 'should not be blank' 58 | end 59 | 60 | describe '.vci' do 61 | pending 'should not be blank' 62 | end 63 | 64 | describe '.url_redirection' do 65 | pending 'should not be blank' 66 | end 67 | 68 | describe '.card_number' do 69 | pending 'should not be blank' 70 | end 71 | 72 | describe '.card_expiration_date' do 73 | # pending 'should not be blank' NOTE: In fact Webpay returns blank 74 | end 75 | 76 | describe '.authorization_code' do 77 | pending 'should not be blank' 78 | end 79 | 80 | describe '.payment_type_code' do 81 | pending 'should not be blank' 82 | end 83 | 84 | describe '.response_code' do 85 | pending 'should not be blank' 86 | end 87 | 88 | describe '.amount' do 89 | pending 'should not be blank' 90 | end 91 | 92 | describe '.shares_number' do 93 | pending 'should not be blank' 94 | end 95 | 96 | describe '.commerce_code' do 97 | pending 'should not be blank' 98 | end 99 | 100 | describe '.approved?' do 101 | pending 'should be truthy' 102 | end 103 | end 104 | 105 | context 'when not' do 106 | it { expect { Order::Normal.init_transaction(token: 'asd') }.to raise_error(WebpayRails::RequestFailed) } 107 | pending 'should raise WebpayRails::InvalidCertificate' 108 | end 109 | end 110 | 111 | describe 'Normal debit payment flow' do 112 | before(:each) { visit new_normal_order_path } 113 | 114 | context 'when it accepted' do 115 | it 'it is sent to the success page' do 116 | # Rails app 117 | fill_in(id: 'order_normal_amount', with: '1000') 118 | find('input[type=submit]').click 119 | 120 | # Webpay 121 | find('input#TBK_TIPO_TARJETA[value=DEBIT_CARD]').set(true) 122 | select('TEST COMMERCE BANK', from: 'TBK_BANCO') 123 | fill_in('TBK_NUMERO_TARJETA', with: '12345678') 124 | click_button('button') 125 | 126 | # Demo Bank 127 | within_frame('transicion') do 128 | fill_in('rutClient', with: '111111111') 129 | fill_in('passwordClient', with: '123') 130 | find('input[type=submit]').click 131 | # It is not necessary because it is already selected by default 132 | # select('Aceptar', from: 'vci') 133 | find('input[type=submit]').click 134 | end 135 | 136 | # Webpay 137 | accept_alert if page.driver.options[:browser] == :firefox 138 | click_button('button4') 139 | accept_alert if page.driver.options[:browser] == :firefox 140 | 141 | # Rails app 142 | expect(page).to have_content('Success transaction') 143 | end 144 | end 145 | 146 | context 'when it rejected' do 147 | it 'it is sent to the failure page' do 148 | # Rails app 149 | fill_in(id: 'order_normal_amount', with: '1000') 150 | find('input[type=submit]').click 151 | 152 | # Webpay 153 | find('input#TBK_TIPO_TARJETA[value=DEBIT_CARD]').set(true) 154 | select('TEST COMMERCE BANK', from: 'TBK_BANCO') 155 | fill_in('TBK_NUMERO_TARJETA', with: '12345678') 156 | click_button('button') 157 | 158 | # Demo Bank 159 | within_frame('transicion') do 160 | fill_in('rutClient', with: '111111111') 161 | fill_in('passwordClient', with: '123') 162 | find('input[type=submit]').click 163 | 164 | select('Rechazar', from: 'vci') 165 | find('input[type=submit]').click 166 | end 167 | 168 | # Webpay 169 | accept_alert if page.driver.options[:browser] == :firefox 170 | 171 | # Rails app 172 | expect(page).to have_content('Failed transaction') 173 | end 174 | end 175 | 176 | context 'when click on the "Anular" button' do 177 | it 'it is sent to the failure page' do 178 | # Rails app 179 | fill_in(id: 'order_normal_amount', with: '1000') 180 | find('input[type=submit]').click 181 | 182 | # Webpay 183 | click_button('button3') 184 | accept_alert if page.driver.options[:browser] == :firefox 185 | 186 | # Rails app 187 | expect(page).to have_content('Failed transaction') 188 | end 189 | end 190 | end 191 | 192 | describe 'Normal credit (without shares) payment flow' do 193 | before(:each) { visit '/' } 194 | 195 | context 'when it accepted' do 196 | it 'it is sent to the success page' do 197 | # Rails app 198 | fill_in(id: 'order_normal_amount', with: '1000') 199 | find('input[type=submit]').click 200 | 201 | # Webpay 202 | find('input#TBK_TIPO_TARJETA[value=CREDIT_CARD]').set(true) 203 | fill_in('TBK_NUMERO_TARJETA', with: '4051885600446623') 204 | fill_in('TBK_CVV', with: '123') 205 | shares_radio = find('input#TBK_TIPO_PAGO597020000541_noshares') 206 | expect(shares_radio).to be_checked 207 | click_button('button') 208 | 209 | # Demo Bank 210 | within_frame('transicion') do 211 | fill_in('rutClient', with: '111111111') 212 | fill_in('passwordClient', with: '123') 213 | find('input[type=submit]').click 214 | # It is not necessary because it is already selected by default 215 | # select('Aceptar', from: 'vci') 216 | find('input[type=submit]').click 217 | end 218 | 219 | # Webpay 220 | accept_alert if page.driver.options[:browser] == :firefox 221 | click_button('button4') 222 | accept_alert if page.driver.options[:browser] == :firefox 223 | 224 | # Rails app 225 | expect(page).to have_content('Success transaction') 226 | end 227 | end 228 | 229 | context 'when it rejected' do 230 | it 'it is sent to the failure page' do 231 | # Rails app 232 | fill_in(id: 'order_normal_amount', with: '1000') 233 | find('input[type=submit]').click 234 | 235 | # Webpay 236 | find('input#TBK_TIPO_TARJETA[value=CREDIT_CARD]').set(true) 237 | fill_in('TBK_NUMERO_TARJETA', with: '4051885600446623') 238 | fill_in('TBK_CVV', with: '123') 239 | shares_radio = find('input#TBK_TIPO_PAGO597020000541_noshares') 240 | expect(shares_radio).to be_checked 241 | click_button('button') 242 | 243 | # Demo Bank 244 | within_frame('transicion') do 245 | fill_in('rutClient', with: '111111111') 246 | fill_in('passwordClient', with: '123') 247 | find('input[type=submit]').click 248 | 249 | select('Rechazar', from: 'vci') 250 | find('input[type=submit]').click 251 | end 252 | 253 | # Webpay 254 | accept_alert if page.driver.options[:browser] == :firefox 255 | 256 | # Rails app 257 | expect(page).to have_content('Failed transaction') 258 | end 259 | end 260 | end 261 | --------------------------------------------------------------------------------