tray-checkout 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +3 -0
- data/README.rdoc +86 -1
- data/lib/tray/checkout/constants.rb +53 -0
- data/lib/tray/checkout/hash.rb +22 -0
- data/lib/tray/checkout/parser.rb +73 -27
- data/lib/tray/checkout/transaction.rb +6 -2
- data/lib/tray/checkout/version.rb +1 -1
- data/lib/tray/checkout/web_service.rb +2 -1
- data/lib/tray-checkout.rb +2 -0
- data/spec/support/mock_request.rb +23 -0
- data/spec/support/responses/create_failure_validation_errors.xml +51 -0
- data/spec/support/responses/create_success_boleto.xml +284 -0
- data/spec/support/responses/create_success_mastercard.xml +272 -0
- data/spec/support/responses/get_failure_not_found.xml +14 -0
- data/spec/support/responses/get_success_boleto.xml +111 -0
- data/spec/tray/checkout/hash_spec.rb +66 -0
- data/spec/tray/checkout/parser_spec.rb +202 -0
- data/spec/tray/checkout/transaction_spec.rb +81 -0
- data/spec/tray/checkout/web_service_spec.rb +16 -0
- metadata +35 -13
data/CHANGELOG.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -7,6 +7,7 @@ Tray Checkout API
|
|
7
7
|
=== Gemfile
|
8
8
|
gem 'tray-checkout'
|
9
9
|
|
10
|
+
|
10
11
|
=== Direct installation
|
11
12
|
$ gem install tray-checkout
|
12
13
|
|
@@ -15,7 +16,91 @@ Tray Checkout API
|
|
15
16
|
|
16
17
|
require 'tray-checkout'
|
17
18
|
|
18
|
-
|
19
|
+
transaction = Tray::Checkout::Transaction.new
|
20
|
+
|
21
|
+
Getting an existing transaction:
|
22
|
+
txn = transaction.get("522045453u5uu32e0u8014f060uuu5uu")
|
23
|
+
txn[:success] # => true
|
24
|
+
txn[:transaction_token] # => "522045453u5uu32e0u8014f060uuu5uu"
|
25
|
+
txn[:transaction_id] # => 501
|
26
|
+
txn[:status] # => :waiting_payment
|
27
|
+
txn[:payment_method] # => :boleto
|
28
|
+
txn[:price_payment] # => 213.21
|
29
|
+
txn[:price_seller] # => 199.19
|
30
|
+
txn[:split] # => 1
|
31
|
+
txn[:payment][:url_payment] # => "http://checkout.sandbox.tray.com.br/payment/billet/u9uuu8731319u59u3073u9011uu6u6uu"
|
32
|
+
txn[:date_transaction].strftime("%d/%m/%Y") # => "03/12/2012"
|
33
|
+
|
34
|
+
When the transaction is not found:
|
35
|
+
txn = transaction.get("xxxxx00000yyyy")
|
36
|
+
txn[:success] # => false
|
37
|
+
txn[:transaction_id] # => nil
|
38
|
+
txn[:errors].first[:code] # => "003042"
|
39
|
+
txn[:errors].first[:message] # => "Transação não encontrada"
|
40
|
+
|
41
|
+
Creating a successful transaction:
|
42
|
+
txn = transaction.create(
|
43
|
+
token_account: "949u5uu9ef36f7u",
|
44
|
+
customer: {
|
45
|
+
name: "Pedro Bonamides",
|
46
|
+
cpf: "18565842673",
|
47
|
+
email: "pedro@bo.com.br",
|
48
|
+
sex: :male,
|
49
|
+
marital_status: :single,
|
50
|
+
contacts: [
|
51
|
+
{ contact_type: :home,
|
52
|
+
number_contact: "1142360873"
|
53
|
+
}
|
54
|
+
],
|
55
|
+
addresses: [
|
56
|
+
{ address_type: :billing,
|
57
|
+
street: "Avenida Pedro Alvares Cabral",
|
58
|
+
number: "123",
|
59
|
+
neighborhood: "Parque Ibirapuera",
|
60
|
+
postal_code: "04094050",
|
61
|
+
city: "São Paulo",
|
62
|
+
state: "SP"
|
63
|
+
}
|
64
|
+
]
|
65
|
+
},
|
66
|
+
transaction: {
|
67
|
+
order_number: "R1245",
|
68
|
+
shipping_type: "Sedex",
|
69
|
+
shipping_price: 13.94,
|
70
|
+
url_notification: "http://prodis.blog.br/tray_notification"
|
71
|
+
},
|
72
|
+
transaction_product: [
|
73
|
+
{ code: "LOGO-8278",
|
74
|
+
quantity: 2,
|
75
|
+
price_unit: 100.99,
|
76
|
+
description: "Logo Prodis"
|
77
|
+
},
|
78
|
+
{ code: "877",
|
79
|
+
quantity: 1,
|
80
|
+
price_unit: 10.00,
|
81
|
+
description: "Outro produto"
|
82
|
+
}
|
83
|
+
],
|
84
|
+
payment: {
|
85
|
+
payment_method: :mastercard,
|
86
|
+
split: 3,
|
87
|
+
card_name: "ZEFINHA NOCEGA",
|
88
|
+
card_number: "5105105105105100",
|
89
|
+
card_expdate_month: "09",
|
90
|
+
card_expdate_year: "2015",
|
91
|
+
card_cvv: "123"
|
92
|
+
}
|
93
|
+
)
|
94
|
+
txn[:success] # => true
|
95
|
+
txn[:transaction_token] # => "87654321u5uu92e4u09876543uuu5uu"
|
96
|
+
txn[:transaction_id] # => 503
|
97
|
+
txn[:status] # => :approved
|
98
|
+
txn[:status_name] # => "Aprovada"
|
99
|
+
txn[:payment_method] # => :mastercard
|
100
|
+
txn[:payment_method_name] # => "Mastercard"
|
101
|
+
txn[:price_payment] # => 213.21
|
102
|
+
txn[:price_seller] # => 199.19
|
103
|
+
txn[:date_payment].strftime("%d/%m/%Y") # => "05/12/2012"
|
19
104
|
|
20
105
|
|
21
106
|
== Author
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Tray
|
4
|
+
module Checkout
|
5
|
+
PAYMENT_METHOD = {
|
6
|
+
diners: 2,
|
7
|
+
visa: 3,
|
8
|
+
mastercard: 4,
|
9
|
+
american_express: 5,
|
10
|
+
boleto: 6,
|
11
|
+
itau_shopline: 7,
|
12
|
+
pagamento_com_saldo: 8,
|
13
|
+
peela: 14,
|
14
|
+
discover: 15,
|
15
|
+
elo: 16
|
16
|
+
}
|
17
|
+
|
18
|
+
TRANSACTION_STATUS = {
|
19
|
+
waiting_payment: 4,
|
20
|
+
processing: 5,
|
21
|
+
approved: 6,
|
22
|
+
canceled: 7,
|
23
|
+
disputing: 24,
|
24
|
+
monitoring: 87,
|
25
|
+
recovering: 88,
|
26
|
+
disapproved: 89
|
27
|
+
}
|
28
|
+
|
29
|
+
CONTACT_TYPE = {
|
30
|
+
home: "H",
|
31
|
+
mobile: "M",
|
32
|
+
work: "W"
|
33
|
+
}
|
34
|
+
|
35
|
+
ADDRESS_TYPE = {
|
36
|
+
billing: "B",
|
37
|
+
delivery: "D"
|
38
|
+
}
|
39
|
+
|
40
|
+
SEX = {
|
41
|
+
male: "M",
|
42
|
+
female: "F"
|
43
|
+
}
|
44
|
+
|
45
|
+
MARITAL_STATUS = {
|
46
|
+
single: "S",
|
47
|
+
married: "M",
|
48
|
+
separated: "A",
|
49
|
+
divorced: "D",
|
50
|
+
widowed: "W"
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
class Hash
|
3
|
+
def symbolize_all_keys!
|
4
|
+
symbolize_all_keys_in self
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def symbolize_all_keys_in(hash)
|
10
|
+
case
|
11
|
+
when hash.is_a?(Array)
|
12
|
+
hash.each { |value| symbolize_all_keys_in(value) }
|
13
|
+
when hash.is_a?(Hash)
|
14
|
+
hash.symbolize_keys!
|
15
|
+
|
16
|
+
hash.each_value do |value|
|
17
|
+
value.symbolize_keys! if value.is_a?(Hash)
|
18
|
+
symbolize_all_keys_in(value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/tray/checkout/parser.rb
CHANGED
@@ -5,48 +5,94 @@ module Tray
|
|
5
5
|
module Checkout
|
6
6
|
class Parser
|
7
7
|
def response(xml)
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
create_response_from(xml)
|
9
|
+
success? ? data : errors
|
10
|
+
end
|
11
|
+
|
12
|
+
def payment_params!(params)
|
13
|
+
customer_params_types! params[:customer]
|
14
|
+
payment_params_types! params[:payment]
|
15
|
+
|
16
|
+
params
|
11
17
|
end
|
12
18
|
|
13
19
|
private
|
14
20
|
|
15
|
-
def
|
16
|
-
|
21
|
+
def customer_params_types!(customer)
|
22
|
+
return unless customer
|
23
|
+
customer[:gender] = SEX[customer[:sex]] if customer[:sex]
|
24
|
+
customer[:relationship] = MARITAL_STATUS[customer[:marital_status]] if customer[:marital_status]
|
25
|
+
contacts_params_types! customer[:contacts]
|
26
|
+
addresses_params_types! customer[:addresses]
|
17
27
|
end
|
18
28
|
|
19
|
-
def
|
20
|
-
|
29
|
+
def payment_params_types!(payment)
|
30
|
+
return unless payment
|
31
|
+
payment[:payment_method_id] = PAYMENT_METHOD[payment[:payment_method]] if payment[:payment_method]
|
32
|
+
end
|
33
|
+
|
34
|
+
def contacts_params_types!(contacts)
|
35
|
+
return unless contacts
|
36
|
+
|
37
|
+
contacts.each do |contact|
|
38
|
+
contact[:type_contact] = CONTACT_TYPE[contact[:contact_type]] if contact[:contact_type]
|
39
|
+
end
|
21
40
|
end
|
22
41
|
|
23
|
-
def
|
24
|
-
|
42
|
+
def addresses_params_types!(addresses)
|
43
|
+
return unless addresses
|
44
|
+
|
45
|
+
addresses.each do |address|
|
46
|
+
address[:type_address] = ADDRESS_TYPE[address[:address_type]] if address[:address_type]
|
47
|
+
end
|
25
48
|
end
|
26
49
|
|
27
|
-
def
|
28
|
-
|
50
|
+
def create_response_from(xml)
|
51
|
+
@response = Hash.from_xml(xml).symbolize_all_keys![:transaction]
|
29
52
|
end
|
30
53
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
when hash.is_a?(Array)
|
35
|
-
puts "### Array #{hash}"
|
36
|
-
puts
|
37
|
-
hash.each { |value| symbolize_all_keys!(value) }
|
38
|
-
when hash.is_a?(Hash)
|
39
|
-
puts "$$$ Hash: #{hash}"
|
40
|
-
puts
|
41
|
-
hash.symbolize_keys!
|
54
|
+
def success?
|
55
|
+
@response[:message_response][:message] == "success"
|
56
|
+
end
|
42
57
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
58
|
+
def data
|
59
|
+
data_response = @response[:data_response][:transaction]
|
60
|
+
transaction_response_types! data_response
|
61
|
+
payment_response_types! data_response
|
62
|
+
date_to_time! data_response
|
63
|
+
data_response[:success] = true
|
64
|
+
data_response
|
65
|
+
end
|
66
|
+
|
67
|
+
def transaction_response_types!(transaction)
|
68
|
+
transaction[:payment_method] = PAYMENT_METHOD.invert[transaction[:payment_method_id]]
|
69
|
+
transaction[:status] = TRANSACTION_STATUS.invert[transaction[:status_id]]
|
70
|
+
end
|
71
|
+
|
72
|
+
def payment_response_types!(transaction)
|
73
|
+
payment = transaction[:payment]
|
74
|
+
payment[:payment_method] = PAYMENT_METHOD.invert[payment[:payment_method_id]]
|
75
|
+
end
|
76
|
+
|
77
|
+
def errors
|
78
|
+
error_response = @response[:error_response]
|
79
|
+
|
80
|
+
if error_response[:validation_errors]
|
81
|
+
error_response[:errors] = error_response.delete(:validation_errors)
|
47
82
|
end
|
48
83
|
|
49
|
-
|
84
|
+
error_response[:success] = false
|
85
|
+
error_response
|
86
|
+
end
|
87
|
+
|
88
|
+
def date_to_time!(hash)
|
89
|
+
hash.each do |key, value|
|
90
|
+
date_to_time!(value) if value.is_a?(Hash)
|
91
|
+
|
92
|
+
if key.to_s.starts_with?("date_") && value
|
93
|
+
hash[key] = (value.to_time rescue value) || value
|
94
|
+
end
|
95
|
+
end
|
50
96
|
end
|
51
97
|
end
|
52
98
|
end
|
@@ -6,8 +6,12 @@ module Tray
|
|
6
6
|
|
7
7
|
def get(token)
|
8
8
|
response = web_service.request!("#{URL}/get_by_token", { token: token })
|
9
|
-
|
10
|
-
|
9
|
+
parser.response(response)
|
10
|
+
end
|
11
|
+
|
12
|
+
def create(params)
|
13
|
+
response = web_service.request!("#{URL}/pay_complete", parser.payment_params!(params))
|
14
|
+
parser.response(response)
|
11
15
|
end
|
12
16
|
|
13
17
|
private
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
require 'active_support/core_ext'
|
2
3
|
require 'net/http'
|
3
4
|
require 'uri'
|
4
5
|
|
@@ -24,7 +25,7 @@ module Tray
|
|
24
25
|
|
25
26
|
def build_request(uri, params)
|
26
27
|
request = Net::HTTP::Post.new(uri.path)
|
27
|
-
request.
|
28
|
+
request.body = params.to_param
|
28
29
|
request
|
29
30
|
end
|
30
31
|
end
|
data/lib/tray-checkout.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
def mock_request_for(response)
|
4
|
+
url = Regexp.new(Tray::Checkout::Transaction::URL)
|
5
|
+
WebMock::API.stub_request(:post, url).to_return(:status => 200, :body => body_for(response))
|
6
|
+
end
|
7
|
+
|
8
|
+
def body_for(response)
|
9
|
+
case response
|
10
|
+
when :get_success_boleto,
|
11
|
+
:get_failure_not_found,
|
12
|
+
:create_success_boleto,
|
13
|
+
:create_success_mastercard,
|
14
|
+
:create_failure_validation_errors
|
15
|
+
read_file_for(response)
|
16
|
+
else
|
17
|
+
response
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_file_for(filename)
|
22
|
+
File.open("#{File.dirname(__FILE__)}/responses/#{filename}.xml").read
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
<transaction>
|
2
|
+
<message_response>
|
3
|
+
<message>error</message>
|
4
|
+
</message_response>
|
5
|
+
<error_response>
|
6
|
+
<validation_errors type="array">
|
7
|
+
<validation_error>
|
8
|
+
<code>1</code>
|
9
|
+
<message_complete>Tipo não pode ficar em branco</message_complete>
|
10
|
+
<message>não pode ficar em branco</message>
|
11
|
+
<field>person_addresses.type_address</field>
|
12
|
+
</validation_error>
|
13
|
+
<validation_error>
|
14
|
+
<code>1</code>
|
15
|
+
<message_complete>Logradouro (Rua, Av, Travessa...) não pode ficar em branco</message_complete>
|
16
|
+
<message>não pode ficar em branco</message>
|
17
|
+
<field>person_addresses.street</field>
|
18
|
+
</validation_error>
|
19
|
+
<validation_error>
|
20
|
+
<code>1</code>
|
21
|
+
<message_complete>Cep não pode ficar em branco</message_complete>
|
22
|
+
<message>não pode ficar em branco</message>
|
23
|
+
<field>person_addresses.postal_code</field>
|
24
|
+
</validation_error>
|
25
|
+
<validation_error>
|
26
|
+
<code>1</code>
|
27
|
+
<message_complete>Cidade não pode ficar em branco</message_complete>
|
28
|
+
<message>não pode ficar em branco</message>
|
29
|
+
<field>person_addresses.city</field>
|
30
|
+
</validation_error>
|
31
|
+
<validation_error>
|
32
|
+
<code>1</code>
|
33
|
+
<message_complete>UF não pode ficar em branco</message_complete>
|
34
|
+
<message>não pode ficar em branco</message>
|
35
|
+
<field>person_addresses.state</field>
|
36
|
+
</validation_error>
|
37
|
+
<validation_error>
|
38
|
+
<code>1</code>
|
39
|
+
<message_complete>Número, Lote não pode ficar em branco</message_complete>
|
40
|
+
<message>não pode ficar em branco</message>
|
41
|
+
<field>person_addresses.number</field>
|
42
|
+
</validation_error>
|
43
|
+
<validation_error>
|
44
|
+
<code>1</code>
|
45
|
+
<message_complete>Bairro não pode ficar em branco</message_complete>
|
46
|
+
<message>não pode ficar em branco</message>
|
47
|
+
<field>person_addresses.neighborhood</field>
|
48
|
+
</validation_error>
|
49
|
+
</validation_errors>
|
50
|
+
</error_response>
|
51
|
+
</transaction>
|