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 +4 -4
- data/README.md +157 -1
- data/lib/generators/transbank_webpay/install/install_generator.rb +11 -0
- data/lib/generators/transbank_webpay/install/templates/transbank_webpay.rb +23 -0
- data/lib/transbank/webpay.rb +7 -2
- data/lib/transbank/webpay/api.rb +6 -0
- data/lib/transbank/webpay/configuration.rb +1 -1
- data/lib/transbank/webpay/document.rb +39 -5
- data/lib/transbank/webpay/exception_response.rb +5 -4
- data/lib/transbank/webpay/exceptions.rb +7 -0
- data/lib/transbank/webpay/helper.rb +80 -0
- data/lib/transbank/webpay/params.rb +13 -0
- data/lib/transbank/webpay/reader.rb +76 -0
- data/lib/transbank/webpay/request.rb +5 -4
- data/lib/transbank/webpay/response.rb +15 -128
- data/lib/transbank/webpay/struct.rb +6 -0
- data/lib/transbank/webpay/validations.rb +74 -0
- data/lib/transbank/webpay/version.rb +1 -1
- data/transbank-webpay.gemspec +1 -2
- metadata +12 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a89c6199367fd33bb85f9870e52153547414ad7
|
4
|
+
data.tar.gz: 63ec216618b2de32cd2af59ff1af1af205c64a96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15aa8ffba7bf1e939921501bf77576b6afc889e36d07c58007748607848fdb2ea76a6628c20061ca7cce0a74674fe499d4c77aa20c15d7f0414d1c0d6eae7683
|
7
|
+
data.tar.gz: 317322dd5edcf986a54a0bdfe8814ca22482c077efd414a2bd38aa469574da93e22f5454a46a7c1b1055a438ea733ec947585dc591130efd28489d1ed0b0b512
|
data/README.md
CHANGED
@@ -1 +1,157 @@
|
|
1
|
-
#
|
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
|
data/lib/transbank/webpay.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
require 'net/https'
|
2
2
|
require 'uri'
|
3
|
-
require '
|
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'
|
data/lib/transbank/webpay/api.rb
CHANGED
@@ -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
|
@@ -1,15 +1,25 @@
|
|
1
1
|
module Transbank
|
2
2
|
module Webpay
|
3
3
|
class Document
|
4
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
4
|
+
attr_reader :exception, :action, :params
|
5
5
|
|
6
|
-
def initialize(exception, action)
|
7
|
-
|
8
|
-
|
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,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(
|
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
|
-
|
4
|
+
include Validations
|
5
|
+
include Reader
|
6
|
+
include Helper
|
6
7
|
|
7
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
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 <<
|
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
|
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.
|
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,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
|
data/transbank-webpay.gemspec
CHANGED
@@ -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 '
|
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.
|
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-
|
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:
|
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: ''
|