transbank-webpay 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe020196d892c3624a23ca1b34120e389f013066
4
- data.tar.gz: a5236458db9a18c0e89c0a9c93217504c6c605a7
3
+ metadata.gz: 7a89c6199367fd33bb85f9870e52153547414ad7
4
+ data.tar.gz: 63ec216618b2de32cd2af59ff1af1af205c64a96
5
5
  SHA512:
6
- metadata.gz: 172bf5d4851e558dab4e08705573539b34c8bb2a2ad75fd65930d133ebfedaf3d9fa0d80184e49a8ac4cb6c7410714b01a21ffd336114c090bdb82c511249f7d
7
- data.tar.gz: f3fbc82cd2a93e6197a3ccd9bc0a9e92f91eece03801236b1418f927e24751ab87d55710dc08079a39b5bd29837929435464060a4aa0932087c1530a3ee4da8f
6
+ metadata.gz: 15aa8ffba7bf1e939921501bf77576b6afc889e36d07c58007748607848fdb2ea76a6628c20061ca7cce0a74674fe499d4c77aa20c15d7f0414d1c0d6eae7683
7
+ data.tar.gz: 317322dd5edcf986a54a0bdfe8814ca22482c077efd414a2bd38aa469574da93e22f5454a46a7c1b1055a438ea733ec947585dc591130efd28489d1ed0b0b512
data/README.md CHANGED
@@ -1 +1,157 @@
1
- # Not ready for production
1
+ # Transbank::Webpay
2
+
3
+ Ruby Implementation of Transbank Webpay API SOAP
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'transbank-webpay'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install transbank-webpay
20
+
21
+ Run the generator:
22
+
23
+ $ rails generate transbank_webpay:install
24
+
25
+ ## Usage
26
+
27
+ **Init Transaction**
28
+
29
+ ```ruby
30
+ response = Transbank::Webpay.init_transaction({
31
+ buy_order: 'A1234567',
32
+ session_id: '123456',
33
+ return_url: 'http://web.com/return',
34
+ final_url: 'http://web.com/finalize',
35
+ amount: 12_000
36
+ })
37
+ => #<Transbank::Webpay::Response valid: true, #<Transbank::Webpay::Struct token="12333b5bcd772565db2cbf36e88eafcca93a95dad29dd8bd6f44d6d37345", url="https://tbk-web.com/initTransactioninitTransaction">>
38
+ response.token
39
+ => "12333b5bcd772565db2cbf36e88eafcca93a95dad29dd8bd6f44d6d37345"
40
+ response.url
41
+ => "https://tbk-web.com/initTransaction"
42
+ response.valid?
43
+ => true
44
+ ```
45
+
46
+ **Get Transaction Result**
47
+
48
+ ```ruby
49
+ response = Transbank::Webpay.get_transaction_result(token)
50
+ => #<Transbank::Webpay::Response valid: true, #<Transbank::Webpay::Struct accounting_date="0234", buy_order="A1234567", card_detail=#<Transbank::Webpay::Struct card_number=3456>, detail_output=#<Transbank::Webpay::Struct shares_number=0, amount=12000, commerce_code=237020000556, buy_order="A1234567", authorization_code=1234, payment_type_code="VN", response_code=0>, session_id=123456, transaction_date=Mon, 10 Jul 2016 10:00:00 -0400, url_redirection="https://tbk-web.com/filtroUnificado/voucher.cgi", vci="TSY">>
51
+ response.accounting_date
52
+ => "0234"
53
+ response.buy_order
54
+ => "A1234567"
55
+ response.card_detail.card_number
56
+ => 3456
57
+ response.detail_output.shares_number
58
+ => 0
59
+ response.detail_output.amount
60
+ => 12000
61
+ response.detail_output.commerce_code
62
+ => 237020000556
63
+ response.detail_output.buy_order
64
+ => "A1234567"
65
+ response.detail_output.authorization_code
66
+ => 1234
67
+ response.detail_output.payment_type_code
68
+ => "VN"
69
+ response.response_code
70
+ => 0
71
+ response.session_id
72
+ => 123456
73
+ response.transaction_date
74
+ => Mon, 10 Jul 2016 10:00:00 -0400
75
+ response.url_redirection
76
+ => "https://tbk-web.com/filtroUnificado/voucher.cgi"
77
+ response.vci
78
+ => "TSY"
79
+ response.valid?
80
+ => true
81
+ ```
82
+
83
+ **Acknowledge Transaction**
84
+
85
+ ```ruby
86
+ response = Transbank::Webpay.acknowledge_transaction(token)
87
+ => #< Transbank::Webpay::Response valid: true>
88
+ response.valid?
89
+ => true
90
+ ```
91
+
92
+ **Nullify**
93
+
94
+ ```ruby
95
+ Transbank::Webpay.nullify({
96
+ authorization_code: 1234,
97
+ authorized_amount: 12_000,
98
+ buy_order: 'A1234567',
99
+ nullify_amount: 12_000
100
+ })
101
+ => #<Transbank::Webpay::Response valid: true, #<Transbank::Webpay::Struct authorization_code=1415234, authorization_date=Mon, 10 Jul 2016 10:00:00 -0400, balance=0, nullified_amount=12000, token="8743gr557f7037005a48487197b0539a021436dabaf485c4947c95347ba1dgdw78">>
102
+
103
+ response.authorization_code
104
+ => 1415234
105
+ response.authorization_date
106
+ => Mon, 10 Jul 2016 10:00:00 -0400
107
+ response.balance
108
+ => 0
109
+ response.nullified_amount
110
+ => 12000
111
+ response.token
112
+ => "8743gr557f7037005a48487197b0539a021436dabaf485c4947c95347ba1dgdw78"
113
+ response.valid?
114
+ => true
115
+ ```
116
+ **Available response methods:**
117
+
118
+
119
+ ```ruby
120
+ response.valid? # true or false if any errors occurred (exceptions included)
121
+ response.errors # errors array
122
+ response.errors_display? # errors for human
123
+ response.exception? # true or false if an exception occurred
124
+ response.exception # exception object
125
+ response.attributes # hash attributes response (token, reverse_code . . .)
126
+ ```
127
+
128
+ ## Configuration
129
+
130
+ First, you need to set up your configuration:
131
+
132
+ `rails generate transbank_webpay:install`
133
+
134
+ Then edit (config/initializers/transbank_webpay.rb):
135
+
136
+ ```ruby
137
+ Transbank::Webpay.configure do |config|
138
+ config.wsdl_transaction_url = 'WEBPAY_SOAP_URL'
139
+ config.wsdl_nullify_url = 'NULLIFY_WEBPAY_SOAP_URL'
140
+ config.cert_path = 'ABSOLUTE_PATH_TO_CRT_FILE'
141
+ config.key_path = 'ABSOLUTE_PATH_TO_KEY_FILE'
142
+ config.server_cert_path = 'ABSOLUTE_PATH_TO_SERVER_CRT_OR_PEM_FILE'
143
+ config.commerce_code = 'COMMERCE_CODE'
144
+ end
145
+ ```
146
+
147
+ ## Contributing
148
+
149
+ 1. Fork it ( https://github.com/[my-github-username]/transbank-webpay/fork )
150
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
151
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
152
+ 4. Push to the branch (`git push origin my-new-feature`)
153
+ 5. Create a new Pull Request
154
+
155
+ ## License
156
+
157
+ transbank-webpay is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -0,0 +1,11 @@
1
+ module TransbankWebpay
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def generate_config_file
7
+ copy_file 'transbank_webpay.rb', 'config/initializers/transbank_webpay.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ Transbank::Webpay.configure do |config|
2
+ config.wsdl_transaction_url = 'WEBPAY_SOAP_URL'
3
+ config.wsdl_nullify_url = 'NULLIFY_WEBPAY_SOAP_URL'
4
+ config.cert_path = 'ABSOLUTE_PATH_TO_CRT_FILE'
5
+ config.key_path = 'ABSOLUTE_PATH_TO_KEY_FILE'
6
+ config.server_cert_path = 'ABSOLUTE_PATH_TO_SERVER_CRT_OR_PEM_FILE'
7
+ config.commerce_code = 'COMMERCE_CODE'
8
+
9
+ # These are the default options for Net::HTTP
10
+ # config.http_options = { read_timeout: 80 }
11
+
12
+ # ignores any exception passed as argument
13
+ # not capture any exception: config.rescue_exceptions []
14
+ # Default is:
15
+ # [
16
+ # Net::ReadTimeout, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
17
+ # EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError
18
+ # ]
19
+ # config.rescue_exceptions = [
20
+ # Net::ReadTimeout, Timeout::Error,
21
+ # Transbank::Webpay::Exceptions::InvalidSignature
22
+ # ]
23
+ end
@@ -1,12 +1,17 @@
1
1
  require 'net/https'
2
2
  require 'uri'
3
- require 'savon'
3
+ require 'nokogiri'
4
4
  require 'signer'
5
- require 'nori'
6
5
  require 'ostruct'
6
+ require 'builder'
7
7
 
8
8
  require 'transbank/webpay/version'
9
9
  require 'transbank/webpay/configuration'
10
+ require 'transbank/webpay/validations'
11
+ require 'transbank/webpay/struct'
12
+ require 'transbank/webpay/reader'
13
+ require 'transbank/webpay/exceptions'
14
+ require 'transbank/webpay/helper'
10
15
  require 'transbank/webpay/exception_response'
11
16
  require 'transbank/webpay/client'
12
17
  require 'transbank/webpay/response'
@@ -21,6 +21,12 @@ module Transbank
21
21
  Request.new(url, :acknowledge_transaction, params).response
22
22
  end
23
23
 
24
+ def nullify(underscore_params = {})
25
+ params = build_nullify_params(underscore_params)
26
+ url = config.wsdl_nullify_url
27
+ Request.new(url, :nullify, params).response
28
+ end
29
+
24
30
  private
25
31
 
26
32
  def config
@@ -2,7 +2,7 @@ module Transbank
2
2
  module Webpay
3
3
  class Configuration
4
4
  attr_accessor :wsdl_transaction_url
5
- attr_accessor :wsdl_cancel_url
5
+ attr_accessor :wsdl_nullify_url
6
6
 
7
7
  attr_accessor :commerce_code
8
8
 
@@ -1,15 +1,25 @@
1
1
  module Transbank
2
2
  module Webpay
3
3
  class Document
4
- attr_reader :unsigned_document, :unsigned_xml
4
+ include Helper
5
+ attr_reader :unsigned_xml
5
6
  XML_HEADER = "<env:Header><wsse:Security xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' wsse:mustUnderstand='1'/></env:Header>".freeze # rubocop:disable LineLength
6
7
  SOAPENV = 'http://schemas.xmlsoap.org/soap/envelope/'.freeze
7
8
 
8
- def initialize(wsdl_url, action, params = {})
9
- client = Savon.client(wsdl: wsdl_url)
10
- xml = client.build_request(action, message: params).body
9
+ NAMESPACES = {
10
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
11
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
12
+ 'xmlns:tns' => 'http://service.wswebpay.webpay.transbank.com/',
13
+ 'xmlns:env' => 'http://schemas.xmlsoap.org/soap/envelope/'
14
+ }.freeze
11
15
 
12
- @unsigned_document = Nokogiri::XML(xml)
16
+ def initialize(action, params = {})
17
+ camelcase_action = camelcase(action).to_sym
18
+ @unsigned_xml = build_xml camelcase_action, params
19
+ end
20
+
21
+ def unsigned_document
22
+ @unsigned_document ||= Nokogiri::XML(unsigned_xml)
13
23
  end
14
24
 
15
25
  def envelope
@@ -47,6 +57,30 @@ module Transbank
47
57
  signer.to_xml
48
58
  end
49
59
 
60
+ private
61
+
62
+ def build_xml(action, params = {})
63
+ xml = Builder::XmlMarkup.new indent: 0
64
+ xml.instruct! :xml, encoding: 'UTF-8'
65
+ xml.env(:Envelope, NAMESPACES) do
66
+ xml.env(:Body) do
67
+ xml.tns(action) do
68
+ params.each do |name, value|
69
+ build_tag(xml, name, value)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ def build_tag(xml, name, value)
77
+ return xml.tag!(name, value) unless value.is_a?(Hash)
78
+
79
+ xml.tag!(name) do
80
+ value.each { |k, v| build_tag(xml, k, v) }
81
+ end
82
+ end
83
+
50
84
  def cert
51
85
  @cert ||= OpenSSL::X509::Certificate.new open(Transbank::Webpay.configuration.cert_path)
52
86
  end
@@ -1,11 +1,12 @@
1
1
  module Transbank
2
2
  module Webpay
3
3
  class ExceptionResponse
4
- attr_accessor :exception, :action
4
+ attr_reader :exception, :action, :params
5
5
 
6
- def initialize(exception, action)
7
- self.exception = exception
8
- self.action = action
6
+ def initialize(exception, action, params)
7
+ @exception = exception
8
+ @action = action
9
+ @params = params
9
10
  end
10
11
 
11
12
  def valid?
@@ -0,0 +1,7 @@
1
+ module Transbank
2
+ module Webpay
3
+ module Exceptions
4
+ class InvalidSignature < SecurityError; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,80 @@
1
+ module Transbank
2
+ module Webpay
3
+ module Helper
4
+ XS_INTEGER = /^[-+]?[1-9]([0-9]*)?$|^0$/
5
+ XS_DATE_TIME = /^-?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?$/
6
+
7
+ def camelcase(underscore)
8
+ underscore
9
+ .to_s
10
+ .gsub(/(?<=_)(\w)/) { Regexp.last_match[1].upcase }
11
+ .gsub(/(?:_)(\w)/, '\1')
12
+ end
13
+
14
+ def underscore(camelcase)
15
+ camelcase
16
+ .to_s
17
+ .gsub(/::/, '/')
18
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
19
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
20
+ .tr("-", "_")
21
+ .downcase
22
+ end
23
+
24
+ def typecasting(value)
25
+ case value
26
+ when 'true'
27
+ true
28
+ when 'false'
29
+ false
30
+ when XS_DATE_TIME
31
+ try_to_convert(value) { |v| DateTime.parse(v) }
32
+ when XS_INTEGER
33
+ try_to_convert(value) { |v| Integer v }
34
+ else
35
+ value
36
+ end
37
+ end
38
+
39
+ def xml_to_hash(xml_io)
40
+ result = Nokogiri::XML(xml_io)
41
+ xml_node_to_hash(result.root)
42
+ end
43
+
44
+ private
45
+
46
+ def xml_node_to_hash(node)
47
+ return Transbank::Webpay::Struct.new if node.nil?
48
+ return node.content unless node.element?
49
+ return if node.children.empty?
50
+
51
+ hash = node.children.each_with_object({}) do |child, h|
52
+ result = xml_node_to_hash(child)
53
+ return result if return_result?(child)
54
+
55
+ assin_values h, result, child
56
+ end
57
+
58
+ Transbank::Webpay::Struct.new hash
59
+ end
60
+
61
+ def return_result?(child)
62
+ child.next_sibling.nil? && child.previous_sibling.nil?
63
+ end
64
+
65
+ def try_to_convert(value)
66
+ yield value
67
+ rescue ArgumentError
68
+ value
69
+ end
70
+
71
+ def assin_values(hash, result, child)
72
+ return if child.name == 'text'
73
+
74
+ key = underscore child.name
75
+ value = typecasting result
76
+ hash.store key, value
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,6 +1,8 @@
1
1
  module Transbank
2
2
  module Webpay
3
3
  module Params
4
+ include Helper
5
+
4
6
  def build_init_transaction_params(underscore_params = {})
5
7
  camelcase_params = {
6
8
  wSTransactionType: 'TR_NORMAL_WS',
@@ -17,6 +19,17 @@ module Transbank
17
19
 
18
20
  { wsInitTransactionInput: camelcase_params }
19
21
  end
22
+
23
+ def build_nullify_params(underscore_params = {})
24
+ camelcase_params = underscore_params.each_with_object({}) do |(key, value), hash|
25
+ new_key = camelcase(key).to_sym
26
+ hash[new_key] = value
27
+ end
28
+
29
+ camelcase_params[:commerceId] = Transbank::Webpay.configuration.commerce_code
30
+
31
+ { nullificationInput: camelcase_params }
32
+ end
20
33
  end
21
34
  end
22
35
  end
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+ module Transbank
3
+ module Webpay
4
+ module Reader
5
+ attr_reader :content, :action
6
+
7
+ RESPONSE_CODE = {
8
+ get_transaction_result: {
9
+ '0' => 'transacción aprobada',
10
+ '-1' => 'rechazo de transacción',
11
+ '-2' => 'transacción debe reintentarse',
12
+ '-3' => 'error en transacción',
13
+ '-4' => 'rechazo de transacción',
14
+ '-5' => 'rechazo por error de tasa',
15
+ '-6' => 'excede cupo máximo mensual',
16
+ '-7' => 'excede límite diario por transacción',
17
+ '-8' => 'rubro no autorizado'
18
+ },
19
+ default: {
20
+ '0' => 'aprobada',
21
+ '-98' => 'Error'
22
+ }
23
+ }.freeze
24
+
25
+ def doc
26
+ @doc ||= Nokogiri::XML body
27
+ end
28
+
29
+ def response_code_display
30
+ RESPONSE_CODE.fetch(action, RESPONSE_CODE[:default]).fetch(response_code, response_code)
31
+ end
32
+
33
+ def body
34
+ content.body
35
+ end
36
+
37
+ def xml_error_display
38
+ xml_error.map { |e| e.text.gsub(/<!--|-->/, '').strip }
39
+ end
40
+
41
+ def attributes?
42
+ xml_return != ""
43
+ end
44
+
45
+ private
46
+
47
+ def xml_return
48
+ @xml_return ||= doc.at_xpath("//return").to_s
49
+ end
50
+
51
+ def signature_decode
52
+ Base64.decode64(signature_node.content)
53
+ end
54
+
55
+ def xml_error
56
+ doc.xpath("//faultstring")
57
+ end
58
+
59
+ def response_code
60
+ doc.xpath("//responseCode").text
61
+ end
62
+
63
+ def signature_node
64
+ doc.at_xpath('//ds:SignatureValue', ds: 'http://www.w3.org/2000/09/xmldsig#')
65
+ end
66
+
67
+ def digest
68
+ doc.at_xpath("//ds:DigestValue", ds: 'http://www.w3.org/2000/09/xmldsig#').text
69
+ end
70
+
71
+ def signed_node
72
+ doc.at_xpath '//ds:SignedInfo', ds: 'http://www.w3.org/2000/09/xmldsig#'
73
+ end
74
+ end
75
+ end
76
+ end
@@ -1,11 +1,12 @@
1
1
  module Transbank
2
2
  module Webpay
3
3
  class Request
4
- attr_accessor :client, :document, :action
4
+ attr_accessor :client, :document, :action, :params
5
5
 
6
6
  def initialize(wsdl_url, action, params = {})
7
+ @params = params
7
8
  @action = action
8
- @document = Document.new(wsdl_url, action, params)
9
+ @document = Document.new(action, params)
9
10
  @client = Client.new wsdl_url
10
11
  end
11
12
 
@@ -13,9 +14,9 @@ module Transbank
13
14
  rescue_exceptions = Transbank::Webpay.configuration.rescue_exceptions
14
15
 
15
16
  @response ||= begin
16
- Response.new client.post(document.to_xml), action
17
+ Response.new client.post(document.to_xml), action, params
17
18
  rescue match_class(rescue_exceptions) => error
18
- ExceptionResponse.new error, action
19
+ ExceptionResponse.new error, action, params
19
20
  end
20
21
  end
21
22
 
@@ -1,94 +1,31 @@
1
- # rubocop:disable all
2
1
  module Transbank
3
2
  module Webpay
4
3
  class Response
5
- attr_accessor :content, :action, :attributes, :errors, :exception
4
+ include Validations
5
+ include Reader
6
+ include Helper
6
7
 
7
- RESPONSE_CODE = {
8
- get_transaction_result: {
9
- '0' => 'transacción aprobada',
10
- '-1' => 'rechazo de transacción',
11
- '-2' => 'transacción debe reintentarse',
12
- '-3' => 'error en transacción',
13
- '-4' => 'rechazo de transacción',
14
- '-5' => 'rechazo por error de tasa',
15
- '-6' => 'excede cupo máximo mensual',
16
- '-7' => 'excede límite diario por transacción',
17
- '-8' => 'rubro no autorizado'
18
- },
19
- default: {
20
- '0' => 'aprobado',
21
- '-98' => 'Error'
22
- }
23
- }
8
+ attr_reader :attributes, :exception, :params
24
9
 
25
- def initialize(content, action)
26
- self.content = content
27
- self.action = action
28
- self.attributes = xml_result
29
- self.errors = []
30
- validate!
31
- end
10
+ def initialize(content, action, params)
11
+ @content = content
12
+ @action = action
13
+ @params = params
14
+ @attributes = xml_to_hash(xml_return)
15
+ @errors = []
32
16
 
33
- def body
34
- content.body
17
+ validate_response!
35
18
  end
36
19
 
37
20
  def http_code
38
21
  content.code
39
22
  end
40
23
 
41
- def doc
42
- @doc ||= Nokogiri::XML body
43
- end
44
-
45
- def xml_result
46
- parser.parse(doc.at_xpath("//return").to_s).fetch( :return, {}).tap do |hash|
47
- hash[:detail_output].tap {|r| r[:amount] = r[:amount].to_i }
48
-
49
- hash.each { |k, v| hash.store(k, OpenStruct.new(v)) if v.is_a?(Hash) }
50
- end
51
- end
52
-
53
- def xml_error
54
- doc.xpath("//faultstring")
55
- end
56
-
57
- def errors_display
58
- errors.join ', '
59
- end
60
-
61
- def valid?
62
- errors.empty?
63
- end
64
-
65
- def signed_node
66
- doc.at_xpath '//ds:SignedInfo', {'ds' => 'http://www.w3.org/2000/09/xmldsig#'}
67
- end
68
-
69
- def signature_node
70
- doc.at_xpath('//ds:SignatureValue', {'ds' => 'http://www.w3.org/2000/09/xmldsig#'})
71
- end
72
-
73
- def signature_decode
74
- Base64.decode64(signature_node.content)
75
- end
76
-
77
- def response_code
78
- @response_code ||= doc.xpath("//responseCode").text
79
- end
80
-
81
- def response_code_display
82
- if response_code.present?
83
- RESPONSE_CODE.fetch(action, RESPONSE_CODE[:default]).fetch(response_code, response_code)
84
- end
85
- end
86
-
87
24
  def inspect
88
25
  result = ["valid: #{valid?}"]
89
- result << attributes_display if attributes.any?
26
+ result << attributes.inspect if attributes?
90
27
  result << "error: \"#{errors_display}\"" if errors.any?
91
- "#<#{self.class} #{result.join(', ')} >"
28
+ "#<#{self.class} #{result.join(', ')}>"
92
29
  end
93
30
 
94
31
  def exception?
@@ -100,62 +37,12 @@ module Transbank
100
37
  end
101
38
 
102
39
  def method_missing(method_name, *args, &block)
103
- attributes[method_name.to_sym] || super
40
+ attributes.respond_to?(method_name) && attributes.send(method_name) || super
104
41
  end
105
42
 
106
43
  def respond_to_missing?(method_name, include_private = false)
107
- attributes.keys.include?(method_name.to_sym) || super
108
- end
109
-
110
- def xml_error_display
111
- xml_error.map { |e| e.text.gsub(/<!--|-->/, '').strip }
112
- end
113
-
114
- private
115
-
116
- def verify
117
- return if signature_node.nil?
118
-
119
- signed_node.add_namespace 'soap', 'http://schemas.xmlsoap.org/soap/envelope/'
120
- signed_node_canonicalize = signed_node.canonicalize Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, ["soap"], nil
121
-
122
- pub_key.verify OpenSSL::Digest::SHA1.new, signature_decode, signed_node_canonicalize
123
- end
124
-
125
- def server_cert
126
- @server_cert ||= begin
127
- path = Transbank::Webpay.configuration.server_cert_path
128
- OpenSSL::X509::Certificate.new File.read(path)
129
- end
130
- end
131
-
132
- def pub_key
133
- server_cert.public_key
134
- end
135
-
136
- def parser
137
- @parser ||= Nori.new convert_tags_to: lambda { |tag| tag.snakecase.to_sym }
138
- end
139
-
140
- def attributes_display
141
- attributes.map{|name, value| "#{name}: \"#{value}\""}.join ', '
142
- end
143
-
144
- def validate!
145
- if response_code.present? and response_code != '0'
146
- self.errors << response_code_display
147
- end
148
-
149
- if content.class != Net::HTTPOK
150
- self.errors += xml_error_display
151
- self.errors << content.message if content.respond_to?(:message) and self.errors.blank?
152
- end
153
-
154
- if (self.errors.blank? || signature_node.present?) && !verify
155
- raise Exceptions::InvalidSignature.new("Invalid signature")
156
- end
44
+ attributes.respond_to?(method_name, include_private) || super
157
45
  end
158
46
  end
159
47
  end
160
48
  end
161
- # rubocop:enable all
@@ -0,0 +1,6 @@
1
+ module Transbank
2
+ module Webpay
3
+ class Struct < OpenStruct
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,74 @@
1
+ module Transbank
2
+ module Webpay
3
+ module Validations
4
+ attr_reader :errors
5
+
6
+ def errors_display
7
+ errors.join ', '
8
+ end
9
+
10
+ def valid?
11
+ errors.empty?
12
+ end
13
+
14
+ def validate_response!
15
+ validate_response_code!
16
+ validate_http_response!
17
+
18
+ return if errors.any? || validate_document
19
+
20
+ raise Transbank::Webpay::Exceptions::InvalidSignature, 'Invalid signature'
21
+ end
22
+
23
+ private
24
+
25
+ def calculated_digest
26
+ node = doc.at_xpath('//soap:Body')
27
+ node_canonicalize = node.canonicalize(Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, nil, nil)
28
+ digest = OpenSSL::Digest::SHA1.new.reset.digest(node_canonicalize)
29
+ Base64.encode64(digest).strip
30
+ end
31
+
32
+ def server_cert
33
+ @server_cert ||= begin
34
+ path = Transbank::Webpay.configuration.server_cert_path
35
+ OpenSSL::X509::Certificate.new File.read(path)
36
+ end
37
+ end
38
+
39
+ def pub_key
40
+ server_cert.public_key
41
+ end
42
+
43
+ # Validations
44
+ def validate_response_code!
45
+ return if response_code.empty?
46
+ @errors << response_code_display if response_code != '0'
47
+ end
48
+
49
+ def validate_http_response!
50
+ return if content.class == Net::HTTPOK
51
+
52
+ @errors += xml_error_display
53
+ @errors << content.message if content.respond_to?(:message) && @errors.empty?
54
+ end
55
+
56
+ def validate_document
57
+ return false if signature_node.nil?
58
+ validate_digest && validate_signature
59
+ end
60
+
61
+ def validate_signature
62
+ signed_node_canonicalize = signed_node.canonicalize(
63
+ Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0, ["soap"], nil
64
+ )
65
+
66
+ pub_key.verify OpenSSL::Digest::SHA1.new, signature_decode, signed_node_canonicalize
67
+ end
68
+
69
+ def validate_digest
70
+ calculated_digest == digest
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,5 +1,5 @@
1
1
  module Transbank
2
2
  module Webpay
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -20,9 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_dependency 'signer'
23
- spec.add_dependency 'savon', '~> 2.0'
24
23
  spec.add_dependency 'nokogiri'
25
- spec.add_dependency 'nori'
24
+ spec.add_dependency 'builder', '>= 2.0'
26
25
 
27
26
  spec.add_development_dependency 'bundler', '~> 1.11'
28
27
  spec.add_development_dependency 'rake', '~> 10.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transbank-webpay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramón Soto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-06-24 00:00:00.000000000 Z
11
+ date: 2016-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: signer
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: savon
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '2.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '2.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: nokogiri
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +39,19 @@ dependencies:
53
39
  - !ruby/object:Gem::Version
54
40
  version: '0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: nori
42
+ name: builder
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - ">="
60
46
  - !ruby/object:Gem::Version
61
- version: '0'
47
+ version: '2.0'
62
48
  type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: '0'
54
+ version: '2.0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: bundler
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +125,8 @@ files:
139
125
  - Rakefile
140
126
  - bin/console
141
127
  - bin/setup
128
+ - lib/generators/transbank_webpay/install/install_generator.rb
129
+ - lib/generators/transbank_webpay/install/templates/transbank_webpay.rb
142
130
  - lib/transbank-webpay.rb
143
131
  - lib/transbank/webpay.rb
144
132
  - lib/transbank/webpay/api.rb
@@ -146,9 +134,14 @@ files:
146
134
  - lib/transbank/webpay/configuration.rb
147
135
  - lib/transbank/webpay/document.rb
148
136
  - lib/transbank/webpay/exception_response.rb
137
+ - lib/transbank/webpay/exceptions.rb
138
+ - lib/transbank/webpay/helper.rb
149
139
  - lib/transbank/webpay/params.rb
140
+ - lib/transbank/webpay/reader.rb
150
141
  - lib/transbank/webpay/request.rb
151
142
  - lib/transbank/webpay/response.rb
143
+ - lib/transbank/webpay/struct.rb
144
+ - lib/transbank/webpay/validations.rb
152
145
  - lib/transbank/webpay/version.rb
153
146
  - transbank-webpay.gemspec
154
147
  homepage: ''