activemerchant 1.64.0 → 1.65.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +27 -0
  3. data/README.md +0 -1
  4. data/lib/active_merchant/billing/gateways/adyen.rb +228 -0
  5. data/lib/active_merchant/billing/gateways/authorize_net.rb +9 -1
  6. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +68 -2
  7. data/lib/active_merchant/billing/gateways/braintree_blue.rb +10 -2
  8. data/lib/active_merchant/billing/gateways/checkout_v2.rb +2 -2
  9. data/lib/active_merchant/billing/gateways/cyber_source.rb +2 -0
  10. data/lib/active_merchant/billing/gateways/global_collect.rb +17 -5
  11. data/lib/active_merchant/billing/gateways/jetpay.rb +12 -9
  12. data/lib/active_merchant/billing/gateways/openpay.rb +1 -0
  13. data/lib/active_merchant/billing/gateways/orbital.rb +5 -3
  14. data/lib/active_merchant/billing/gateways/payeezy.rb +7 -0
  15. data/lib/active_merchant/billing/gateways/payu_latam.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/pin.rb +5 -0
  17. data/lib/active_merchant/billing/gateways/quickpay.rb +3 -3
  18. data/lib/active_merchant/billing/gateways/qvalent.rb +44 -1
  19. data/lib/active_merchant/billing/gateways/safe_charge.rb +211 -0
  20. data/lib/active_merchant/billing/gateways/sage_pay.rb +8 -5
  21. data/lib/active_merchant/billing/gateways/stripe.rb +6 -1
  22. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +45 -11
  23. data/lib/active_merchant/billing/gateways/wepay.rb +1 -0
  24. data/lib/active_merchant/billing/gateways/worldpay.rb +8 -3
  25. data/lib/active_merchant/posts_data.rb +1 -1
  26. data/lib/active_merchant/version.rb +1 -1
  27. metadata +4 -3
  28. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +0 -314
@@ -686,6 +686,8 @@ module ActiveMerchant #:nodoc:
686
686
  response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(request, options)))
687
687
  rescue ResponseError => e
688
688
  response = parse(e.response.body)
689
+ rescue REXML::ParseException => e
690
+ response = { message: e.to_s }
689
691
  end
690
692
 
691
693
  success = response[:decision] == "ACCEPT"
@@ -131,8 +131,8 @@ module ActiveMerchant #:nodoc:
131
131
  if payment
132
132
  post["order"]["customer"]["personalInformation"] = {
133
133
  "name" => {
134
- "firstName" => payment.first_name,
135
- "surname" => payment.last_name
134
+ "firstName" => payment.first_name[0..14],
135
+ "surname" => payment.last_name[0..69]
136
136
  }
137
137
  }
138
138
  end
@@ -262,14 +262,20 @@ EOS
262
262
  end
263
263
 
264
264
  def success_from(response)
265
- !response["errorId"]
265
+ !response["errorId"] && response["status"] != "REJECTED"
266
266
  end
267
267
 
268
268
  def message_from(succeeded, response)
269
269
  if succeeded
270
270
  "Succeeded"
271
271
  else
272
- response["errors"][0]["message"] || "Unable to read error message"
272
+ if errors = response["errors"]
273
+ errors.first.try(:[], "message")
274
+ elsif status = response["status"]
275
+ "Status: " + status
276
+ else
277
+ "No message available"
278
+ end
273
279
  end
274
280
  end
275
281
 
@@ -283,7 +289,13 @@ EOS
283
289
 
284
290
  def error_code_from(succeeded, response)
285
291
  unless succeeded
286
- response["errors"][0]["code"] || "Unable to read error code"
292
+ if errors = response["errors"]
293
+ errors.first.try(:[], "code")
294
+ elsif status = response.try(:[], "statusOutput").try(:[], "statusCode")
295
+ status.to_s
296
+ else
297
+ "No error code available"
298
+ end
287
299
  end
288
300
  end
289
301
 
@@ -165,12 +165,15 @@ module ActiveMerchant #:nodoc:
165
165
  end
166
166
 
167
167
  def capture(money, reference, options = {})
168
- commit(money, build_capture_request(reference.split(";").first, money, options))
168
+ split_authorization = reference.split(";")
169
+ transaction_id = split_authorization[0]
170
+ token = split_authorization[3]
171
+ commit(money, build_capture_request(transaction_id, money, options), token)
169
172
  end
170
173
 
171
174
  def void(reference, options = {})
172
175
  transaction_id, approval, amount, token = reference.split(";")
173
- commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options))
176
+ commit(amount.to_i, build_void_request(amount.to_i, transaction_id, approval, token, options), token)
174
177
  end
175
178
 
176
179
  def credit(money, transaction_id_or_card, options = {})
@@ -183,9 +186,9 @@ module ActiveMerchant #:nodoc:
183
186
  end
184
187
 
185
188
  def refund(money, reference, options = {})
186
- params = reference.split(";")
187
- transaction_id = params[0]
188
- token = params[3]
189
+ split_authorization = reference.split(";")
190
+ transaction_id = split_authorization[0]
191
+ token = split_authorization[3]
189
192
  credit_card = options[:credit_card]
190
193
  commit(money, build_credit_request('CREDIT', money, transaction_id, credit_card, token, options))
191
194
  end
@@ -279,7 +282,7 @@ module ActiveMerchant #:nodoc:
279
282
  end
280
283
  end
281
284
 
282
- def commit(money, request)
285
+ def commit(money, request, token = nil)
283
286
  response = parse(ssl_post(url, request))
284
287
 
285
288
  success = success?(response)
@@ -287,7 +290,7 @@ module ActiveMerchant #:nodoc:
287
290
  success ? 'APPROVED' : message_from(response),
288
291
  response,
289
292
  :test => test?,
290
- :authorization => authorization_from(response, money),
293
+ :authorization => authorization_from(response, money, token),
291
294
  :avs_result => { :code => response[:avs] },
292
295
  :cvv_result => response[:cvv2]
293
296
  )
@@ -330,9 +333,9 @@ module ActiveMerchant #:nodoc:
330
333
  ACTION_CODE_MESSAGES[response[:action_code]]
331
334
  end
332
335
 
333
- def authorization_from(response, money)
336
+ def authorization_from(response, money, previous_token)
334
337
  original_amount = amount(money) if money
335
- [ response[:transaction_id], response[:approval], original_amount, response[:token]].join(";")
338
+ [ response[:transaction_id], response[:approval], original_amount, (response[:token] || previous_token)].join(";")
336
339
  end
337
340
 
338
341
  def add_credit_card(xml, credit_card)
@@ -113,6 +113,7 @@ module ActiveMerchant #:nodoc:
113
113
  post[:order_id] = options[:order_id]
114
114
  post[:device_session_id] = options[:device_session_id]
115
115
  post[:currency] = (options[:currency] || currency(money)).upcase
116
+ post[:use_card_points] = options[:use_card_points] if options[:use_card_points]
116
117
  add_creditcard(post, creditcard, options)
117
118
  post
118
119
  end
@@ -417,10 +417,12 @@ module ActiveMerchant #:nodoc:
417
417
  # Do not submit the attribute at all.
418
418
  # - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf
419
419
  unless creditcard.nil?
420
- if %w( visa discover ).include?(creditcard.brand)
421
- xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9')
420
+ if creditcard.verification_value?
421
+ if %w( visa discover ).include?(creditcard.brand)
422
+ xml.tag! :CardSecValInd, '1'
423
+ end
424
+ xml.tag! :CardSecVal, creditcard.verification_value
422
425
  end
423
- xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
424
426
  end
425
427
  end
426
428
 
@@ -37,6 +37,7 @@ module ActiveMerchant
37
37
  add_payment_method(params, payment_method)
38
38
  add_address(params, options)
39
39
  add_amount(params, amount, options)
40
+ add_soft_descriptors(params, options)
40
41
 
41
42
  commit(params, options)
42
43
  end
@@ -48,6 +49,7 @@ module ActiveMerchant
48
49
  add_payment_method(params, payment_method)
49
50
  add_address(params, options)
50
51
  add_amount(params, amount, options)
52
+ add_soft_descriptors(params, options)
51
53
 
52
54
  commit(params, options)
53
55
  end
@@ -57,6 +59,7 @@ module ActiveMerchant
57
59
 
58
60
  add_authorization_info(params, authorization)
59
61
  add_amount(params, amount, options)
62
+ add_soft_descriptors(params, options)
60
63
 
61
64
  commit(params, options)
62
65
  end
@@ -169,6 +172,10 @@ module ActiveMerchant
169
172
  params[:amount] = amount(money)
170
173
  end
171
174
 
175
+ def add_soft_descriptors(params, options)
176
+ params[:soft_descriptors] = options[:soft_descriptors] if options[:soft_descriptors]
177
+ end
178
+
172
179
  def commit(params, options)
173
180
  url = if options[:integration]
174
181
  integration_url
@@ -45,7 +45,7 @@ module ActiveMerchant #:nodoc:
45
45
  commit('auth', post)
46
46
  end
47
47
 
48
- def capture(authorization, options={})
48
+ def capture(amount, authorization, options={})
49
49
  post = {}
50
50
 
51
51
  add_credentials(post, 'SUBMIT_TRANSACTION')
@@ -29,6 +29,7 @@ module ActiveMerchant #:nodoc:
29
29
  add_creditcard(post, creditcard)
30
30
  add_address(post, creditcard, options)
31
31
  add_capture(post, options)
32
+ add_metadata(post, options)
32
33
 
33
34
  commit(:post, 'charges', post, options)
34
35
  end
@@ -141,6 +142,10 @@ module ActiveMerchant #:nodoc:
141
142
  end
142
143
  end
143
144
 
145
+ def add_metadata(post, options)
146
+ post[:metadata] = options[:metadata] if options[:metadata]
147
+ end
148
+
144
149
  def headers(params = {})
145
150
  result = {
146
151
  "Content-Type" => "application/json",
@@ -10,16 +10,16 @@ module ActiveMerchant #:nodoc:
10
10
  self.abstract_class = true
11
11
 
12
12
  def self.new(options = {})
13
- options.fetch(:login) rescue raise ArgumentError.new("Missing required parameter: login")
13
+ options.fetch(:login) { raise ArgumentError.new("Missing required parameter: login") }
14
14
 
15
15
  version = options[:login].to_i < 10000000 ? 10 : 7
16
16
  if version <= 7
17
17
  QuickpayV4to7Gateway.new(options)
18
18
  else
19
- QuickpayV10Gateway.new(options)
19
+ QuickpayV10Gateway.new(options)
20
20
  end
21
21
  end
22
-
22
+
23
23
  end
24
24
  end
25
25
  end
@@ -13,7 +13,7 @@ module ActiveMerchant #:nodoc:
13
13
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners]
14
14
 
15
15
  def initialize(options={})
16
- requires!(options, :username, :password, :merchant)
16
+ requires!(options, :username, :password, :merchant, :pem, :pem_password)
17
17
  super
18
18
  end
19
19
 
@@ -24,19 +24,52 @@ module ActiveMerchant #:nodoc:
24
24
  add_payment_method(post, payment_method)
25
25
  add_verification_value(post, payment_method)
26
26
  add_customer_data(post, options)
27
+ add_soft_descriptors(post, options)
27
28
 
28
29
  commit("capture", post)
29
30
  end
30
31
 
32
+ def authorize(amount, payment_method, options={})
33
+ post = {}
34
+ add_invoice(post, amount, options)
35
+ add_order_number(post, options)
36
+ add_payment_method(post, payment_method)
37
+ add_verification_value(post, payment_method)
38
+ add_customer_data(post, options)
39
+ add_soft_descriptors(post, options)
40
+
41
+ commit("preauth", post)
42
+ end
43
+
44
+ def capture(amount, authorization, options={})
45
+ post = {}
46
+ add_invoice(post, amount, options)
47
+ add_reference(post, authorization, options)
48
+ add_customer_data(post, options)
49
+ add_soft_descriptors(post, options)
50
+
51
+ commit("captureWithoutAuth", post)
52
+ end
53
+
31
54
  def refund(amount, authorization, options={})
32
55
  post = {}
33
56
  add_invoice(post, amount, options)
34
57
  add_reference(post, authorization, options)
35
58
  add_customer_data(post, options)
59
+ add_soft_descriptors(post, options)
36
60
 
37
61
  commit("refund", post)
38
62
  end
39
63
 
64
+ def void(authorization, options={})
65
+ post = {}
66
+ add_reference(post, authorization, options)
67
+ add_customer_data(post, options)
68
+ add_soft_descriptors(post, options)
69
+
70
+ commit("reversal", post)
71
+ end
72
+
40
73
  def store(payment_method, options = {})
41
74
  post = {}
42
75
  add_payment_method(post, payment_method)
@@ -62,6 +95,16 @@ module ActiveMerchant #:nodoc:
62
95
  CURRENCY_CODES["AUD"] = "AUD"
63
96
  CURRENCY_CODES["INR"] = "INR"
64
97
 
98
+ def add_soft_descriptors(post, options)
99
+ post["customer.merchantName"] = options[:customer_merchant_name] if options[:customer_merchant_name]
100
+ post["customer.merchantStreetAddress"] = options[:customer_merchant_street_address] if options[:customer_merchant_street_address]
101
+ post["customer.merchantLocation"] = options[:customer_merchant_location] if options[:customer_merchant_location]
102
+ post["customer.merchantState"] = options[:customer_merchant_state] if options[:customer_merchant_state]
103
+ post["customer.merchantCountry"] = options[:customer_merchant_country] if options[:customer_merchant_country]
104
+ post["customer.merchantPostCode"] = options[:customer_merchant_post_code] if options[:customer_merchant_post_code]
105
+ post["customer.subMerchantId"] = options[:customer_sub_merchant_id] if options[:customer_sub_merchant_id]
106
+ end
107
+
65
108
  def add_invoice(post, money, options)
66
109
  post["order.amount"] = amount(money)
67
110
  post["card.currency"] = CURRENCY_CODES[options[:currency] || currency(money)]
@@ -0,0 +1,211 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class SafeChargeGateway < Gateway
6
+ self.test_url = 'https://process.sandbox.safecharge.com/service.asmx/Process'
7
+ self.live_url = 'https://process.safecharge.com/service.asmx/Process'
8
+
9
+ self.supported_countries = ['US']
10
+ self.default_currency = 'USD'
11
+ self.supported_cardtypes = [:visa, :master]
12
+
13
+ self.homepage_url = 'https://www.safecharge.com'
14
+ self.display_name = 'SafeCharge'
15
+
16
+ VERSION = '4.0.4'
17
+
18
+ def initialize(options={})
19
+ requires!(options, :client_login_id, :client_password)
20
+ super
21
+ end
22
+
23
+ def purchase(money, payment, options={})
24
+ post = {}
25
+ add_transaction_data("Sale", post, money, options)
26
+ add_payment(post, payment)
27
+
28
+ commit(post)
29
+ end
30
+
31
+ def authorize(money, payment, options={})
32
+ post = {}
33
+ add_transaction_data("Auth", post, money, options)
34
+ add_payment(post, payment)
35
+
36
+ commit(post)
37
+ end
38
+
39
+ def capture(money, authorization, options={})
40
+ post = {}
41
+ add_transaction_data("Settle", post, money, options)
42
+ auth, transaction_id, token, exp_month, exp_year, _ = authorization.split("|")
43
+ post[:sg_AuthCode] = auth
44
+ post[:sg_TransactionID] = transaction_id
45
+ post[:sg_CCToken] = token
46
+ post[:sg_ExpMonth] = exp_month
47
+ post[:sg_ExpYear] = exp_year
48
+
49
+ commit(post)
50
+ end
51
+
52
+ def refund(money, authorization, options={})
53
+ post = {}
54
+ add_transaction_data("Credit", post, money, options)
55
+ auth, transaction_id, token, exp_month, exp_year, _ = authorization.split("|")
56
+ post[:sg_CreditType] = 2
57
+ post[:sg_AuthCode] = auth
58
+ post[:sg_TransactionID] = transaction_id
59
+ post[:sg_CCToken] = token
60
+ post[:sg_ExpMonth] = exp_month
61
+ post[:sg_ExpYear] = exp_year
62
+
63
+ commit(post)
64
+ end
65
+
66
+ def void(authorization, options={})
67
+ post = {}
68
+ auth, transaction_id, token, exp_month, exp_year, original_amount = authorization.split("|")
69
+ add_transaction_data("Void", post, (original_amount.to_f * 100), options)
70
+ post[:sg_CreditType] = 2
71
+ post[:sg_AuthCode] = auth
72
+ post[:sg_TransactionID] = transaction_id
73
+ post[:sg_CCToken] = token
74
+ post[:sg_ExpMonth] = exp_month
75
+ post[:sg_ExpYear] = exp_year
76
+
77
+ commit(post)
78
+ end
79
+
80
+ def verify(credit_card, options={})
81
+ MultiResponse.run(:use_first_response) do |r|
82
+ r.process { authorize(100, credit_card, options) }
83
+ r.process(:ignore_result) { void(r.authorization, options) }
84
+ end
85
+ end
86
+
87
+ def supports_scrubbing?
88
+ true
89
+ end
90
+
91
+ def scrub(transcript)
92
+ transcript.
93
+ gsub(%r((sg_ClientPassword=)[^&]+(&?)), '\1[FILTERED]\2').
94
+ gsub(%r((sg_CardNumber=)[^&]+(&?)), '\1[FILTERED]\2').
95
+ gsub(%r((sg_CVV2=)\d+), '\1[FILTERED]')
96
+ end
97
+
98
+ private
99
+
100
+ def add_transaction_data(trans_type, post, money, options)
101
+ post[:sg_TransType] = trans_type
102
+ post[:sg_Currency] = (options[:currency] || currency(money))
103
+ post[:sg_Amount] = amount(money)
104
+ post[:sg_ClientLoginID] = @options[:client_login_id]
105
+ post[:sg_ClientPassword] = @options[:client_password]
106
+ post[:sg_ResponseFormat] = "4"
107
+ post[:sg_Version] = VERSION
108
+ end
109
+
110
+ def add_payment(post, payment)
111
+ post[:sg_NameOnCard] = payment.name
112
+ post[:sg_CardNumber] = payment.number
113
+ post[:sg_ExpMonth] = format(payment.month, :two_digits)
114
+ post[:sg_ExpYear] = format(payment.year, :two_digits)
115
+ post[:sg_CVV2] = payment.verification_value
116
+ end
117
+
118
+ def parse(xml)
119
+ response = {}
120
+
121
+ doc = Nokogiri::XML(xml)
122
+ doc.root.xpath('*').each do |node|
123
+ response[node.name.underscore.downcase.to_sym] = node.text
124
+ end
125
+
126
+ response
127
+ end
128
+
129
+ def childnode_to_response(response, node, childnode)
130
+ name = "#{node.name.downcase}_#{childnode.name.downcase}"
131
+ if name == 'payment_method_data' && !childnode.elements.empty?
132
+ response[name.to_sym] = Hash.from_xml(childnode.to_s).values.first
133
+ else
134
+ response[name.to_sym] = childnode.text
135
+ end
136
+ end
137
+
138
+ def commit(parameters)
139
+ url = (test? ? test_url : live_url)
140
+ response = parse(ssl_post(url, post_data(parameters)))
141
+
142
+ Response.new(
143
+ success_from(response),
144
+ message_from(response),
145
+ response,
146
+ authorization: authorization_from(response, parameters),
147
+ avs_result: AVSResult.new(code: response[:avs_code]),
148
+ cvv_result: CVVResult.new(response[:cvv2_reply]),
149
+ test: test?,
150
+ error_code: error_code_from(response)
151
+ )
152
+ end
153
+
154
+ def success_from(response)
155
+ response[:status] == "APPROVED"
156
+ end
157
+
158
+ def message_from(response)
159
+ return "Success" if success_from(response)
160
+ response[:reason_codes] || response[:reason]
161
+ end
162
+
163
+ def authorization_from(response, parameters)
164
+ [
165
+ response[:auth_code],
166
+ response[:transaction_id],
167
+ response[:token],
168
+ parameters[:sg_ExpMonth],
169
+ parameters[:sg_ExpYear],
170
+ parameters[:sg_Amount]
171
+ ].join("|")
172
+ end
173
+
174
+ def split_authorization(authorization)
175
+ auth_code, transaction_id, token, month, year, original_amount = authorization.split("|")
176
+
177
+ {
178
+ auth_code: auth_code,
179
+ transaction_id: transaction_id,
180
+ token: token,
181
+ exp_month: month,
182
+ exp_year: year,
183
+ original_amount: amount(original_amount.to_f * 100)
184
+ }
185
+ end
186
+
187
+ def post_data(params)
188
+ return nil unless params
189
+
190
+ params.map do |key, value|
191
+ next if value != false && value.blank?
192
+ "#{key}=#{CGI.escape(value.to_s)}"
193
+ end.compact.join("&")
194
+ end
195
+
196
+ def error_code_from(response)
197
+ unless success_from(response)
198
+ response[:ex_err_code] || response[:err_code]
199
+ end
200
+ end
201
+
202
+ def underscore(camel_cased_word)
203
+ camel_cased_word.to_s.gsub(/::/, '/').
204
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
205
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
206
+ tr("-", "_").
207
+ downcase
208
+ end
209
+ end
210
+ end
211
+ end