activemerchant 1.64.0 → 1.65.0

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.
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