activemerchant 1.59.0 → 1.60.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +41 -0
  3. data/README.md +2 -2
  4. data/lib/active_merchant/billing/gateway.rb +3 -1
  5. data/lib/active_merchant/billing/gateways/authorize_net.rb +16 -5
  6. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +6 -4
  7. data/lib/active_merchant/billing/gateways/braintree_blue.rb +19 -3
  8. data/lib/active_merchant/billing/gateways/clearhaus.rb +6 -35
  9. data/lib/active_merchant/billing/gateways/cyber_source.rb +20 -8
  10. data/lib/active_merchant/billing/gateways/data_cash.rb +10 -304
  11. data/lib/active_merchant/billing/gateways/elavon.rb +40 -26
  12. data/lib/active_merchant/billing/gateways/global_transport.rb +1 -0
  13. data/lib/active_merchant/billing/gateways/maxipago.rb +144 -122
  14. data/lib/active_merchant/billing/gateways/openpay.rb +1 -0
  15. data/lib/active_merchant/billing/gateways/orbital.rb +3 -1
  16. data/lib/active_merchant/billing/gateways/pagarme.rb +248 -0
  17. data/lib/active_merchant/billing/gateways/psl_card.rb +3 -3
  18. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +0 -2
  20. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +1 -1
  21. data/lib/active_merchant/billing/gateways/redsys.rb +1 -0
  22. data/lib/active_merchant/billing/gateways/sage_pay.rb +24 -7
  23. data/lib/active_merchant/billing/gateways/stripe.rb +21 -11
  24. data/lib/active_merchant/billing/gateways/tns.rb +11 -2
  25. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/visanet_peru.rb +19 -28
  27. data/lib/active_merchant/version.rb +1 -1
  28. metadata +3 -3
  29. data/lib/active_merchant/billing/gateways/certo_direct.rb +0 -278
@@ -0,0 +1,248 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PagarmeGateway < Gateway
4
+ self.live_url = 'https://api.pagar.me/1/'
5
+
6
+ self.supported_countries = ['BR']
7
+ self.default_currency = 'BRL'
8
+ self.money_format = :cents
9
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club]
10
+
11
+ self.homepage_url = 'https://pagar.me/'
12
+ self.display_name = 'Pagar.me'
13
+
14
+ STANDARD_ERROR_CODE_MAPPING = {
15
+ 'refused' => STANDARD_ERROR_CODE[:card_declined],
16
+ 'processing_error' => STANDARD_ERROR_CODE[:processing_error],
17
+ }
18
+
19
+ def initialize(options={})
20
+ requires!(options, :api_key)
21
+ @api_key = options[:api_key]
22
+
23
+ super
24
+ end
25
+
26
+ def purchase(money, payment_method, options={})
27
+ post = {}
28
+ add_amount(post, money)
29
+ add_payment_method(post, payment_method)
30
+ add_metadata(post, options)
31
+
32
+ commit(:post, 'transactions', post)
33
+ end
34
+
35
+ def authorize(money, payment_method, options={})
36
+ post = {}
37
+ add_amount(post, money)
38
+ add_payment_method(post, payment_method)
39
+ add_metadata(post, options)
40
+
41
+ post[:capture] = false
42
+
43
+ commit(:post, 'transactions', post)
44
+ end
45
+
46
+ def capture(money, authorization, options={})
47
+ if authorization.nil?
48
+ return Response.new(false, 'Não é possível capturar uma transação sem uma prévia autorização.')
49
+ end
50
+
51
+ post = {}
52
+ commit(:post, "transactions/#{authorization}/capture", post)
53
+ end
54
+
55
+ def refund(money, authorization, options={})
56
+ if authorization.nil?
57
+ return Response.new(false, 'Não é possível estornar uma transação sem uma prévia captura.')
58
+ end
59
+
60
+ void(authorization, options)
61
+ end
62
+
63
+ def void(authorization, options={})
64
+ if authorization.nil?
65
+ return Response.new(false, 'Não é possível estornar uma transação autorizada sem uma prévia autorização.')
66
+ end
67
+
68
+ post = {}
69
+ commit(:post, "transactions/#{authorization}/refund", post)
70
+ end
71
+
72
+ def verify(payment_method, options={})
73
+ MultiResponse.run(:use_first_response) do |r|
74
+ r.process { authorize(127, payment_method, options) }
75
+ r.process(:ignore_result) { void(r.authorization, options) }
76
+ end
77
+ end
78
+
79
+ def supports_scrubbing?
80
+ true
81
+ end
82
+
83
+ def scrub(transcript)
84
+ transcript.
85
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
86
+ gsub(%r((card_number=)\d+), '\1[FILTERED]').
87
+ gsub(%r((card_cvv=)\d+), '\1[FILTERED]')
88
+ end
89
+
90
+ private
91
+
92
+ def add_amount(post, money)
93
+ post[:amount] = amount(money)
94
+ end
95
+
96
+ def add_payment_method(post, payment_method)
97
+ post[:payment_method] = 'credit_card'
98
+ add_credit_card(post, payment_method)
99
+ end
100
+
101
+ def add_credit_card(post, credit_card)
102
+ post[:card_number] = credit_card.number
103
+ post[:card_holder_name] = credit_card.name
104
+ post[:card_expiration_date] = "#{credit_card.month}/#{credit_card.year}"
105
+ post[:card_cvv] = credit_card.verification_value
106
+ end
107
+
108
+ def add_metadata(post, options={})
109
+ post[:metadata] = {}
110
+ post[:metadata][:order_id] = options[:order_id]
111
+ post[:metadata][:ip] = options[:ip]
112
+ post[:metadata][:customer] = options[:customer]
113
+ post[:metadata][:invoice] = options[:invoice]
114
+ post[:metadata][:merchant] = options[:merchant]
115
+ post[:metadata][:description] = options[:description]
116
+ post[:metadata][:email] = options[:email]
117
+ end
118
+
119
+ def parse(body)
120
+ JSON.parse(body)
121
+ end
122
+
123
+ def post_data(params)
124
+ return nil unless params
125
+
126
+ params.map do |key, value|
127
+ next if value != false && value.blank?
128
+ if value.is_a?(Hash)
129
+ h = {}
130
+ value.each do |k, v|
131
+ h["#{key}[#{k}]"] = v unless v.blank?
132
+ end
133
+ post_data(h)
134
+ elsif value.is_a?(Array)
135
+ value.map { |v| "#{key}[]=#{CGI.escape(v.to_s)}" }.join("&")
136
+ else
137
+ "#{key}=#{CGI.escape(value.to_s)}"
138
+ end
139
+ end.compact.join("&")
140
+ end
141
+
142
+ def headers(options = {})
143
+ {
144
+ "Authorization" => "Basic " + Base64.encode64(@api_key.to_s + ":x").strip,
145
+ "User-Agent" => "Pagar.me/1 ActiveMerchant/#{ActiveMerchant::VERSION}",
146
+ "Accept-Encoding" => "deflate"
147
+ }
148
+ end
149
+
150
+ def api_request(method, endpoint, parameters = nil, options = {})
151
+ raw_response = response = nil
152
+ begin
153
+ raw_response = ssl_request(method, self.live_url + endpoint, post_data(parameters), headers(options))
154
+ response = parse(raw_response)
155
+ rescue ResponseError => e
156
+ raw_response = e.response.body
157
+ response = response_error(raw_response)
158
+ rescue JSON::ParserError
159
+ response = json_error(raw_response)
160
+ end
161
+ response
162
+ end
163
+
164
+ def commit(method, url, parameters, options = {})
165
+ response = api_request(method, url, parameters, options)
166
+
167
+ Response.new(
168
+ success_from(response),
169
+ message_from(response),
170
+ response,
171
+ authorization: authorization_from(response),
172
+ test: test?,
173
+ error_code: error_code_from(response)
174
+ )
175
+ end
176
+
177
+ def response_error(raw_response)
178
+ begin
179
+ parse(raw_response)
180
+ rescue JSON::ParserError
181
+ json_error(raw_response)
182
+ end
183
+ end
184
+
185
+ def json_error(raw_response)
186
+ msg = 'Resposta inválida retornada pela API do Pagar.me. Por favor entre em contato com suporte@pagar.me se você continuar recebendo essa mensagem.'
187
+ msg += " (A resposta retornada pela API foi #{raw_response.inspect})"
188
+ {
189
+ "errors" => [{
190
+ "message" => msg
191
+ }]
192
+ }
193
+ end
194
+
195
+ def success_from(response)
196
+ success_purchase = response.key?("status") && response["status"] == "paid"
197
+ success_authorize = response.key?("status") && response["status"] == "authorized"
198
+ success_refund = response.key?("status") && response["status"] == "refunded"
199
+
200
+ success_purchase || success_authorize || success_refund
201
+ end
202
+
203
+ def failure_from(response)
204
+ response.key?("status") && response["status"] == "refused"
205
+ end
206
+
207
+ def message_from(response)
208
+ if success_from(response)
209
+ case response["status"]
210
+ when "paid"
211
+ "Transação aprovada"
212
+ when "authorized"
213
+ "Transação autorizada"
214
+ when "refunded"
215
+ "Transação estornada"
216
+ else
217
+ "Transação com status '#{response["status"]}'"
218
+ end
219
+ elsif failure_from(response)
220
+ "Transação recusada"
221
+ elsif response.key?("errors")
222
+ response["errors"][0]["message"]
223
+ else
224
+ msg = json_error(response)
225
+ msg["errors"][0]["message"]
226
+ end
227
+ end
228
+
229
+ def authorization_from(response)
230
+ if success_from(response)
231
+ response["id"]
232
+ end
233
+ end
234
+
235
+ def test?()
236
+ @api_key.start_with?("ak_test")
237
+ end
238
+
239
+ def error_code_from(response)
240
+ if failure_from(response)
241
+ STANDARD_ERROR_CODE_MAPPING["refused"]
242
+ elsif response.key?("errors")
243
+ STANDARD_ERROR_CODE_MAPPING["processing_error"]
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -101,7 +101,7 @@ module ActiveMerchant
101
101
  # -options:
102
102
  #
103
103
  # Returns:
104
- # -ActiveRecord::Billing::Response object
104
+ # -ActiveMerchant::Billing::Response object
105
105
  #
106
106
  def purchase(money, credit_card, options = {})
107
107
  post = {}
@@ -129,7 +129,7 @@ module ActiveMerchant
129
129
  # -options:
130
130
  #
131
131
  # Returns:
132
- # -ActiveRecord::Billing::Response object
132
+ # -ActiveMerchant::Billing::Response object
133
133
  #
134
134
  def authorize(money, credit_card, options = {})
135
135
  post = {}
@@ -153,7 +153,7 @@ module ActiveMerchant
153
153
  # -options:
154
154
  #
155
155
  # Returns:
156
- # -ActiveRecord::Billing::Response object
156
+ # -ActiveMerchant::Billing::Response object
157
157
  #
158
158
  def capture(money, authorization, options = {})
159
159
  post = {}
@@ -141,7 +141,7 @@ module QuickpayCommon
141
141
 
142
142
  10 => {
143
143
  :authorize => %w(mobile_number acquirer autofee customer_id extras
144
- zero_auth),
144
+ zero_auth customer_ip),
145
145
  :capture => %w( extras ),
146
146
  :cancel => %w( extras ),
147
147
  :refund => %w( extras ),
@@ -118,10 +118,8 @@ module ActiveMerchant
118
118
  end
119
119
 
120
120
  def authorize_store(identification, credit_card, options = {})
121
- requires!(options, :amount)
122
121
  post = {}
123
122
 
124
- add_amount(post, nil, options)
125
123
  add_credit_card_or_reference(post, credit_card, options)
126
124
  commit(synchronized_path("/cards/#{identification}/authorize"), post)
127
125
  end
@@ -136,7 +136,7 @@ module ActiveMerchant #:nodoc:
136
136
  end
137
137
 
138
138
  def add_description(post, options)
139
- post[:description] = options[:description]
139
+ post[:description] = options[:description] || "Description"
140
140
  end
141
141
 
142
142
  def add_testmode(post)
@@ -70,6 +70,7 @@ module ActiveMerchant #:nodoc:
70
70
  "PEN" => '604',
71
71
  "PLN" => '616',
72
72
  "RUB" => '643',
73
+ "SAR" => '682',
73
74
  "SEK" => '752',
74
75
  "SGD" => '702',
75
76
  "THB" => '764',
@@ -43,6 +43,26 @@ module ActiveMerchant #:nodoc:
43
43
  "NOTMATCHED" => 'N'
44
44
  }
45
45
 
46
+ OPTIONAL_REQUEST_FIELDS = {
47
+ paypal_callback_url: :PayPalCallbackURL,
48
+ basket: :Basket,
49
+ gift_aid_payment: :GiftAidPayment ,
50
+ apply_avscv2: :ApplyAVSCV2 ,
51
+ apply_3d_secure: :Apply3DSecure,
52
+ account_type: :AccountType,
53
+ billing_agreement: :BillingAgreement,
54
+ basket_xml: :BasketXML,
55
+ customer_xml: :CustomerXML,
56
+ surcharge_xml: :SurchargeXML,
57
+ vendor_data: :VendorData,
58
+ language: :Language,
59
+ website: :Website,
60
+ recipient_account_number: :FIRecipientAcctNumber ,
61
+ recipient_surname: :FIRecipientSurname ,
62
+ recipient_postcode: :FIRecipientPostcode ,
63
+ recipient_dob: :FIRecipientDoB
64
+ }
65
+
46
66
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :switch, :solo, :maestro, :diners_club]
47
67
  self.supported_countries = ['GB', 'IE']
48
68
  self.default_currency = 'GBP'
@@ -195,14 +215,11 @@ module ActiveMerchant #:nodoc:
195
215
  end
196
216
 
197
217
  def add_optional_data(post, options)
198
- add_pair(post, :GiftAidPayment, options[:gift_aid_payment]) unless options[:gift_aid_payment].blank?
199
- add_pair(post, :ApplyAVSCV2, options[:apply_avscv2]) unless options[:apply_avscv2].blank?
200
- add_pair(post, :Apply3DSecure, options[:apply_3d_secure]) unless options[:apply_3d_secure].blank?
201
218
  add_pair(post, :CreateToken, 1) unless options[:store].blank?
202
- add_pair(post, :FIRecipientAcctNumber, options[:recipient_account_number])
203
- add_pair(post, :FIRecipientSurname, options[:recipient_surname])
204
- add_pair(post, :FIRecipientPostcode, options[:recipient_postcode])
205
- add_pair(post, :FIRecipientDoB, options[:recipient_dob])
219
+
220
+ OPTIONAL_REQUEST_FIELDS.each do |gateway_option, sagepay_field|
221
+ add_pair(post, sagepay_field, options[gateway_option])
222
+ end
206
223
  end
207
224
 
208
225
  def add_address(post, options)
@@ -21,7 +21,7 @@ module ActiveMerchant #:nodoc:
21
21
  'unchecked' => 'P'
22
22
  }
23
23
 
24
- self.supported_countries = %w(AT AU BE CA CH DE DK ES FI FR GB IE IT LU NL NO SE US)
24
+ self.supported_countries = %w(AT AU BE CA CH DE DK ES FI FR GB IE IT LU NL NO SE SG US)
25
25
  self.default_currency = 'USD'
26
26
  self.money_format = :cents
27
27
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
@@ -42,7 +42,8 @@ module ActiveMerchant #:nodoc:
42
42
  'card_declined' => STANDARD_ERROR_CODE[:card_declined],
43
43
  'call_issuer' => STANDARD_ERROR_CODE[:call_issuer],
44
44
  'processing_error' => STANDARD_ERROR_CODE[:processing_error],
45
- 'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin]
45
+ 'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin],
46
+ 'test_mode_live_card' => STANDARD_ERROR_CODE[:test_mode_live_card]
46
47
  }
47
48
 
48
49
  BANK_ACCOUNT_HOLDER_TYPE_MAPPING = {
@@ -167,16 +168,15 @@ module ActiveMerchant #:nodoc:
167
168
  params = {}
168
169
  post = {}
169
170
 
170
- if card_brand(payment) == "check"
171
- bank_token_response = tokenize_bank_account(payment)
172
- if bank_token_response.success?
173
- params = { source: bank_token_response.params["token"]["id"] }
174
- else
175
- return bank_token_response
176
- end
177
- elsif payment.is_a?(ApplePayPaymentToken)
171
+ if payment.is_a?(ApplePayPaymentToken)
178
172
  token_exchange_response = tokenize_apple_pay_token(payment)
179
173
  params = { card: token_exchange_response.params["token"]["id"] } if token_exchange_response.success?
174
+ elsif payment.is_a?(StripePaymentToken)
175
+ add_payment_token(params, payment, options)
176
+ elsif payment.is_a?(Check)
177
+ bank_token_response = tokenize_bank_account(payment)
178
+ return bank_token_response unless bank_token_response.success?
179
+ params = { source: bank_token_response.params["token"]["id"] }
180
180
  else
181
181
  add_creditcard(params, payment, options)
182
182
  end
@@ -237,6 +237,16 @@ module ActiveMerchant #:nodoc:
237
237
  end
238
238
  end
239
239
 
240
+ def verify_credentials
241
+ begin
242
+ ssl_get(live_url + "charges/nonexistent", headers)
243
+ rescue ResponseError => e
244
+ return false if e.response.code.to_i == 401
245
+ end
246
+
247
+ true
248
+ end
249
+
240
250
  def supports_scrubbing?
241
251
  true
242
252
  end
@@ -277,7 +287,6 @@ module ActiveMerchant #:nodoc:
277
287
  unless emv_payment?(payment)
278
288
  add_amount(post, money, options, true)
279
289
  add_customer_data(post, options)
280
- add_metadata(post, options)
281
290
  post[:description] = options[:description]
282
291
  post[:statement_descriptor] = options[:statement_description]
283
292
  post[:receipt_email] = options[:receipt_email] if options[:receipt_email]
@@ -285,6 +294,7 @@ module ActiveMerchant #:nodoc:
285
294
  add_flags(post, options)
286
295
  end
287
296
 
297
+ add_metadata(post, options)
288
298
  add_application_fee(post, options)
289
299
  add_destination(post, options)
290
300
  post
@@ -1,19 +1,27 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class TnsGateway < Gateway
4
+ class_attribute :live_na_url, :live_ap_url
5
+
4
6
  self.display_name = 'TNS'
5
7
  self.homepage_url = 'http://www.tnsi.com/'
6
8
 
7
9
  # Testing is partitioned by account.
8
- self.live_url = 'https://secure.na.tnspayments.com/api/rest/version/22/'
10
+ self.live_na_url = 'https://secure.na.tnspayments.com/api/rest/version/22/'
11
+ self.live_ap_url = 'https://secure.ap.tnspayments.com/api/rest/version/22/'
9
12
 
10
13
  self.supported_countries = %w(AR AU BR FR DE HK MX NZ SG GB US)
11
14
 
12
15
  self.default_currency = 'USD'
13
16
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :maestro, :laser]
14
17
 
18
+ self.ssl_version = :TLSv1_2
19
+
15
20
  def initialize(options={})
16
21
  requires!(options, :userid, :password)
22
+
23
+ options[:region] = 'north_america' unless options[:region]
24
+
17
25
  super
18
26
  end
19
27
 
@@ -174,7 +182,8 @@ module ActiveMerchant #:nodoc:
174
182
  end
175
183
 
176
184
  def build_url(orderid, transactionid)
177
- "#{live_url}merchant/#{@options[:userid]}/order/#{orderid}/transaction/#{transactionid}"
185
+ base_url = @options[:region] == 'asia_pacific' ? live_ap_url : live_na_url
186
+ "#{base_url}merchant/#{@options[:userid]}/order/#{orderid}/transaction/#{transactionid}"
178
187
  end
179
188
 
180
189
  def build_request(post = {})