braspag-pagador 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.md +23 -0
  6. data/README.md +532 -0
  7. data/RELEASES.md +20 -0
  8. data/Rakefile +6 -0
  9. data/braspag-pagador.gemspec +32 -0
  10. data/coverage/.resultset.json +2732 -0
  11. data/coverage/assets/0.5.3/app.js +88 -0
  12. data/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
  13. data/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
  14. data/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
  15. data/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
  16. data/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
  17. data/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
  18. data/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
  19. data/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
  20. data/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
  21. data/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
  22. data/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
  23. data/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
  24. data/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
  25. data/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
  26. data/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
  27. data/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
  28. data/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
  29. data/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
  30. data/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
  31. data/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
  32. data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +363 -0
  33. data/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +44 -0
  34. data/coverage/assets/0.5.3/favicon_green.png +0 -0
  35. data/coverage/assets/0.5.3/favicon_red.png +0 -0
  36. data/coverage/assets/0.5.3/favicon_yellow.png +0 -0
  37. data/coverage/assets/0.5.3/highlight.css +129 -0
  38. data/coverage/assets/0.5.3/highlight.pack.js +1 -0
  39. data/coverage/assets/0.5.3/jquery-1.6.2.min.js +18 -0
  40. data/coverage/assets/0.5.3/jquery.dataTables.min.js +152 -0
  41. data/coverage/assets/0.5.3/jquery.timeago.js +141 -0
  42. data/coverage/assets/0.5.3/jquery.url.js +174 -0
  43. data/coverage/assets/0.5.3/loading.gif +0 -0
  44. data/coverage/assets/0.5.3/magnify.png +0 -0
  45. data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  46. data/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  47. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  48. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  49. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  50. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  51. data/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  52. data/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  53. data/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
  54. data/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  55. data/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
  56. data/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
  57. data/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  58. data/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +295 -0
  59. data/coverage/assets/0.5.3/stylesheet.css +383 -0
  60. data/coverage/index.html +17219 -0
  61. data/lib/braspag.rb +96 -0
  62. data/lib/braspag/core/connection.rb +112 -0
  63. data/lib/braspag/core/converter.rb +63 -0
  64. data/lib/braspag/core/customer.rb +13 -0
  65. data/lib/braspag/core/order.rb +286 -0
  66. data/lib/braspag/core/poster.rb +40 -0
  67. data/lib/braspag/crypto/no_crypto.rb +6 -0
  68. data/lib/braspag/crypto/webservice.rb +63 -0
  69. data/lib/braspag/payment/billet.rb +79 -0
  70. data/lib/braspag/payment/credit_card.rb +190 -0
  71. data/lib/braspag/payment/eft.rb +65 -0
  72. data/lib/braspag/payment/recurrency_credit_card.rb +15 -0
  73. data/lib/braspag/templates/crypto/decrypt.xml.erb +10 -0
  74. data/lib/braspag/templates/crypto/encrypt.xml.erb +14 -0
  75. data/lib/braspag/templates/justclick/archive.xml.erb +16 -0
  76. data/lib/braspag/templates/justclick/get_recurrency.xml.erb +12 -0
  77. data/lib/braspag/templates/justclick/recurrency.xml.erb +23 -0
  78. data/lib/braspag/version.rb +3 -0
  79. data/spec/core/connection_spec.rb +149 -0
  80. data/spec/core/converter_spec.rb +123 -0
  81. data/spec/core/customer_spec.rb +49 -0
  82. data/spec/core/order_spec.rb +504 -0
  83. data/spec/core/poster_spec.rb +63 -0
  84. data/spec/crypto/webservice_spec.rb +136 -0
  85. data/spec/integration/billet_spec.rb +38 -0
  86. data/spec/integration/credit_card_spec.rb +0 -0
  87. data/spec/payment/billet_spec.rb +205 -0
  88. data/spec/payment/credit_card_spec.rb +385 -0
  89. data/spec/payment/eft_spec.rb +88 -0
  90. data/spec/payment/recurrency_credit_card_spec.rb +100 -0
  91. data/spec/spec_helper.rb +24 -0
  92. metadata +292 -0
@@ -0,0 +1,40 @@
1
+ module Braspag
2
+ class Poster
3
+ def initialize(connection, url)
4
+ @connection = connection
5
+ @request = ::HTTPI::Request.new(url)
6
+ end
7
+
8
+ def do_post(method, data, headers = {})
9
+ @request.body = data
10
+ @request.proxy = @connection.proxy_address if @connection.proxy_address
11
+ headers.each_pair do |k,v|
12
+ @request.headers[k] = v
13
+ end
14
+
15
+ with_logger(method) do
16
+ ::HTTPI.post @request
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def with_logger(method)
23
+ if @connection.logger
24
+ @connection.logger.info("[Braspag] ##{method}: #{@request.url}, data: #{mask_data(@request.body).inspect}")
25
+ response = yield
26
+ @connection.logger.info("[Braspag] ##{method} returns: #{response.body.inspect}")
27
+ else
28
+ response = yield
29
+ end
30
+ response
31
+ end
32
+
33
+ def mask_data(data)
34
+ copy_data = data.dup
35
+ copy_data['cardNumber'] = "************%s" % copy_data['cardNumber'][-4..-1] if copy_data['cardNumber']
36
+ copy_data['securityCode'] = "***" if copy_data['securityCode']
37
+ copy_data
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,6 @@
1
+ module Braspag
2
+ module Crypto
3
+ class NoCrypto
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ module Braspag
2
+ module Crypto
3
+ class Webservice
4
+ def encrypt(connection, map)
5
+ data = ERB.new(File.read(Braspag::PATH + '/braspag/templates/crypto/encrypt.xml.erb'))
6
+
7
+ response = Braspag::Poster.new(
8
+ connection,
9
+ connection.url_for(:encrypt)
10
+ ).do_post(
11
+ :encrypt,
12
+ data.result(binding),
13
+ {"Content-Type" => "text/xml"}
14
+ )
15
+
16
+ document = Nokogiri::XML(response.body)
17
+
18
+ raise 'UnknownError' if document.children.empty?
19
+
20
+ #melhorar este parser cof cof
21
+ response = document.children.children.children.children.children.to_s
22
+
23
+ raise 'InvalidMerchantId' if (response == 'Erro BP 011' || response == 'Erro BP 012')
24
+ raise 'InvalidIP' if (response == 'Erro BP 067' || response == 'Erro BP 068')
25
+
26
+ response
27
+ end
28
+
29
+ def decrypt(connection, encripted_text)
30
+ data = ERB.new(File.read(Braspag::PATH + '/braspag/templates/crypto/decrypt.xml.erb'))
31
+
32
+ response = Braspag::Poster.new(
33
+ connection,
34
+ connection.url_for(:decrypt)
35
+ ).do_post(
36
+ :decrypt,
37
+ data.result(binding),
38
+ {"Content-Type" => "text/xml"}
39
+ )
40
+
41
+ document = Nokogiri::XML(response.body)
42
+ raise 'UnknownError' if document.children.empty?
43
+
44
+ result_error = document.children.children.children.children.children.first.content.to_s
45
+
46
+ raise 'InvalidMerchantId' if (result_error == 'Erro BP 011' || result_error == 'Erro BP 012')
47
+ raise 'InvalidIP' if (result_error == 'Erro BP 067' || result_error == 'Erro BP 068')
48
+
49
+ convert_request_to_map document
50
+ end
51
+
52
+ protected
53
+ def convert_request_to_map(document)
54
+ map = {}
55
+ document.children.children.children.children.children.each do |n|
56
+ values = n.content.to_s.split("=")
57
+ map[values[0].downcase.to_sym] = values[1] if values.size == 2
58
+ end
59
+ map
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,79 @@
1
+ require "bigdecimal"
2
+
3
+ module Braspag
4
+ class Connection
5
+ def generate_billet(order, billet)
6
+ response = self.post(:generate_billet, order, billet)
7
+ status = (response[:status] == "0")
8
+
9
+ ActiveMerchant::Billing::Response.new(status,
10
+ response[:message],
11
+ response,
12
+ :test => homologation?,
13
+ :authorization => response[:number])
14
+ end
15
+ end
16
+
17
+ class Billet
18
+ include ::ActiveAttr::Model
19
+
20
+ class DueDateValidator < ActiveModel::EachValidator
21
+ def validate_each(record, attribute, value)
22
+ # unless value.class.in? [Date, Time]
23
+ unless (
24
+ value.kind_of?(Time) || value.kind_of?(Date)
25
+ )
26
+ record.errors.add attribute, "invalid date"
27
+ end
28
+ end
29
+ end
30
+
31
+ attr_accessor :id, :instructions, :due_date_on, :url, :code, :created_at, :due_date_on
32
+ attr_accessor :receiver, :bank, :agency, :account, :wallet, :amount, :amount_paid, :paid_at
33
+
34
+ validates :id, :length => {:minimum => 1, :maximum => 255, :on => :generate, :allow_blank => true }
35
+ validates :instructions, :length => {:minimum => 1, :maximum => 512, :on => :generate, :allow_blank => true }
36
+ validates :due_date_on, :presence => { :on => :generate }
37
+ validates :due_date_on, :due_date => { :on => :generate }
38
+
39
+ def self.to_generate_billet(connection, order, billet)
40
+ {
41
+ "merchantId" => connection.merchant_id,
42
+ "boletoNumber" => billet.id.to_s,
43
+ "instructions" => billet.instructions.to_s,
44
+ "expirationDate" => billet.due_date_on.strftime("%d/%m/%y"),
45
+ "customerName" => order.customer.name.to_s,
46
+ "customerIdNumber" => order.customer.document.to_s,
47
+ "emails" => order.customer.email.to_s,
48
+ "orderId" => order.id.to_s,
49
+ "amount" => Braspag::Converter::decimal_to_string(order.amount),
50
+ "paymentMethod" => order.payment_method
51
+ }
52
+ end
53
+
54
+ def self.from_generate_billet(connection, order, billet, params)
55
+ response = Braspag::Converter::hash_from_xml(params.body, {
56
+ :url => nil,
57
+ :amount => nil,
58
+ :number => "boletoNumber",
59
+ :expiration_date => Proc.new { |document|
60
+ begin
61
+ Date.parse(document.search("expirationDate").first.to_s)
62
+ rescue
63
+ nil
64
+ end
65
+ },
66
+ :return_code => "returnCode",
67
+ :status => nil,
68
+ :message => nil
69
+ })
70
+
71
+ order.gateway_return_code = response[:return_code]
72
+ order.gateway_status = response[:status]
73
+ order.gateway_amount = BigDecimal.new(response[:amount].to_s) if response[:amount]
74
+ billet.url = response[:url]
75
+
76
+ response
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,190 @@
1
+ module Braspag
2
+ class Connection
3
+ def purchase(order, credit_card)
4
+ resp = self.authorize(order, credit_card)
5
+ resp = self.capture(order) if resp.success?
6
+ resp
7
+ end
8
+
9
+ def authorize(order, credit_card)
10
+ response = self.post(:authorize, order, credit_card)
11
+
12
+ status = (response[:status] == "0" || response[:status] == "1")
13
+
14
+ ActiveMerchant::Billing::Response.new(status,
15
+ response[:message],
16
+ response,
17
+ :test => homologation?,
18
+ :authorization => response[:number])
19
+ end
20
+
21
+ def capture(order)
22
+ response = self.post(:capture, order)
23
+
24
+ status = (response[:status] == "0")
25
+
26
+ ActiveMerchant::Billing::Response.new(status,
27
+ response[:message],
28
+ response,
29
+ :test => homologation?,
30
+ :authorization => response[:number])
31
+ end
32
+
33
+ def void(order, partial=nil)
34
+ response = self.post(:void, order)
35
+
36
+ status = (response[:status] == "0")
37
+
38
+ ActiveMerchant::Billing::Response.new(status,
39
+ response[:message],
40
+ response,
41
+ :test => homologation?)
42
+ end
43
+ end
44
+
45
+ class CreditCard
46
+ include ::ActiveAttr::Model
47
+
48
+ attr_accessor :holder_name, :number, :month, :year, :verification_value, :alias, :id
49
+ attr_accessor :checking_number, :avs, :autorization_number, :transaction_number
50
+ attr_accessor :avs_response, :issuing, :authenticated_number
51
+
52
+ class ExpiratorValidator < ActiveModel::EachValidator
53
+ def validate_each(record, attribute, value)
54
+ begin
55
+ year = record.year.try(:to_i)
56
+ year = "20#{year}".to_i if year && year.to_s.size == 2
57
+
58
+ month = record.month.try(:to_i)
59
+
60
+ Date.new(year, month) if year && month
61
+ rescue ArgumentError
62
+ record.errors.add attribute, "invalid date"
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ [:purchase, :authorize, :archive].each do |check_on|
69
+ validates :holder_name, :length => {:minimum => 1, :maximum => 100, :on => check_on}
70
+
71
+ validates :number, :presence => { :on => check_on }
72
+
73
+ validates :month, :presence => { :on => check_on }
74
+ validates :month, :expirator => { :on => check_on }
75
+ validates :year, :presence => { :on => check_on }
76
+ validates :year, :expirator => { :on => check_on }
77
+ end
78
+
79
+ [:purchase, :authorize, :recurrency].each do |check_on|
80
+ validates :verification_value, :length => {:minimum => 1, :maximum => 4, :on => check_on}
81
+ end
82
+
83
+ [:get_recurrency, :recurrency].each do |check_on|
84
+ validates :id, :length => {:is => 36, :on => check_on}
85
+ end
86
+
87
+ def self.to_save_credit_card(connection,credit_card,customer,request_id)
88
+ {
89
+ 'saveCreditCardRequestWS' => {
90
+ 'MerchantKey' => connection.merchant_id,
91
+ 'CustomerName' => customer.name.to_s,
92
+ 'CardHolder' => credit_card.holder_name.to_s,
93
+ 'CardNumber' => credit_card.number.to_s,
94
+ 'CardExpiration' => "#{credit_card.month}/#{credit_card.year}",
95
+ 'RequestId' => request_id
96
+ }
97
+ }
98
+ end
99
+
100
+ def self.to_authorize(connection, order, credit_card)
101
+ year_normalize = credit_card.year.to_s[-2, 2]
102
+ {
103
+ "merchantId" => connection.merchant_id,
104
+ "holder" => credit_card.holder_name.to_s,
105
+ "cardNumber" => credit_card.number.to_s,
106
+ "expiration" => "#{credit_card.month}/#{year_normalize}",
107
+ "securityCode" => credit_card.verification_value.to_s,
108
+ "customerName" => order.customer.name.to_s,
109
+ "orderId" => order.id.to_s,
110
+ "amount" => Braspag::Converter::decimal_to_string(order.amount),
111
+ "paymentMethod" => order.payment_method,
112
+ "numberPayments" => order.installments,
113
+ "typePayment" => order.installments_type
114
+ }
115
+ end
116
+
117
+ def self.from_authorize(connection, order, credit_card, params)
118
+ response = Braspag::Converter::hash_from_xml(params.body, {
119
+ :amount => nil,
120
+ :number => "authorisationNumber",
121
+ :message => nil,
122
+ :return_code => 'returnCode',
123
+ :status => nil,
124
+ :transaction_id => "transactionId"
125
+ })
126
+
127
+ order.gateway_authorization = response[:number]
128
+ order.gateway_id = response[:transaction_id]
129
+ order.gateway_return_code = response[:return_code]
130
+ order.gateway_status = response[:status]
131
+ order.gateway_message = response[:message]
132
+ order.gateway_amount = Braspag::Converter::string_to_decimal(response[:amount])
133
+
134
+ response
135
+ end
136
+
137
+ def self.to_capture(connection, order)
138
+ {
139
+ "merchantId" => connection.merchant_id,
140
+ "orderId" => order.id.to_s
141
+ }
142
+ end
143
+
144
+ def self.from_capture(connection, order, params)
145
+ response = Braspag::Converter::hash_from_xml(params.body, {
146
+ :amount => nil,
147
+ :message => 'message',
148
+ :return_code => 'returnCode',
149
+ :status => 'status',
150
+ :transaction_id => "transactionId"
151
+ })
152
+
153
+ #TODO: CHECK IF IS NECESSARY
154
+ # order.gateway_capture_id = response[:transaction_id]
155
+ order.gateway_capture_return_code = response[:return_code]
156
+ order.gateway_capture_status = response[:status]
157
+ order.gateway_capture_message = response[:message]
158
+ order.gateway_capture_amount = Braspag::Converter::string_to_decimal(response[:amount])
159
+
160
+ response
161
+ end
162
+
163
+ def self.to_void(connection, order)
164
+ {
165
+ "merchantId" => connection.merchant_id,
166
+ "order" => order.id.to_s
167
+ }
168
+ end
169
+
170
+ def self.from_void(connection, order, params)
171
+ response = Braspag::Converter::hash_from_xml(params.body, {
172
+ :order_id => "orderId",
173
+ :amount => nil,
174
+ :message => 'message',
175
+ :return_code => 'returnCode',
176
+ :status => 'status',
177
+ :transaction_id => "transactionId"
178
+ })
179
+
180
+ #TODO: CHECK IF IS NECESSARY
181
+ # order.gateway_void_id = response[:transaction_id]
182
+ order.gateway_void_return_code = response[:return_code]
183
+ order.gateway_void_status = response[:status]
184
+ order.gateway_void_message = response[:message]
185
+ order.gateway_void_amount = Braspag::Converter::string_to_decimal(response[:amount])
186
+
187
+ response
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,65 @@
1
+ module Braspag
2
+ class Connection
3
+ def generate_eft(order, eft)
4
+
5
+ params = {
6
+ Id_Loja: self.merchant_id,
7
+ VALOR: Braspag::Converter::decimal_to_string(order.amount),
8
+ CODPAGAMENTO: order.payment_method,
9
+ VENDAID: order.id,
10
+ NOME: order.customer.name
11
+ }
12
+
13
+ html = "<form id='form_tef_#{order.id}' name='form_tef_#{order.id}' action='#{self.url_for(:generate_eft)}' method='post'>"
14
+
15
+
16
+ begin
17
+ unless eft.crypto.respond_to?(:encrypt)
18
+ params.each do |key, value|
19
+ html << "<input type='text' name='#{key}' value='#{value}' />"
20
+ end
21
+ else
22
+ params.delete("Id_Loja")
23
+ html << "<input type='text' name='Id_Loja' value='#{self.merchant_id}' />"
24
+ html << "<input type='text' name='crypt' value='#{eft.crypto.encrypt(self, params)}' />"
25
+ end
26
+
27
+ html << "</form><script type='text/javascript' charset='utf-8'>document.forms['form_tef_#{order.id}'].submit();</script>"
28
+
29
+ eft.code = html
30
+ status = true
31
+ message = 'OK'
32
+ rescue Exception => e
33
+ status = false
34
+ message = e.message
35
+ end
36
+
37
+ ActiveMerchant::Billing::Response.new(status,
38
+ message,
39
+ {},
40
+ :test => homologation?)
41
+ end
42
+ end
43
+
44
+ class EFT
45
+ include ::ActiveAttr::Model
46
+
47
+ class CryptoValidator < ActiveModel::EachValidator
48
+ def validate_each(record, attribute, value)
49
+ unless (
50
+ value.kind_of?(Braspag::Crypto::NoCrypto) ||
51
+ value.respond_to?(:encrypt)
52
+ )
53
+ record.errors.add attribute, "invalid crypto"
54
+ end
55
+ end
56
+ end
57
+
58
+ attr_accessor :crypto, :code
59
+
60
+ validates :crypto, :presence => { :on => :generate }
61
+ validates :crypto, :crypto => { :on => :generate }
62
+
63
+ end
64
+
65
+ end