activemerchant 1.52.0 → 1.53.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +56 -0
  3. data/README.md +1 -1
  4. data/lib/active_merchant/billing/gateways/authorize_net.rb +4 -1
  5. data/lib/active_merchant/billing/gateways/banwire.rb +11 -0
  6. data/lib/active_merchant/billing/gateways/beanstream.rb +12 -1
  7. data/lib/active_merchant/billing/gateways/blue_pay.rb +519 -506
  8. data/lib/active_merchant/billing/gateways/borgun.rb +10 -0
  9. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +13 -1
  10. data/lib/active_merchant/billing/gateways/braintree_blue.rb +2 -2
  11. data/lib/active_merchant/billing/gateways/braintree_orange.rb +0 -1
  12. data/lib/active_merchant/billing/gateways/card_stream.rb +14 -19
  13. data/lib/active_merchant/billing/gateways/cecabank.rb +11 -1
  14. data/lib/active_merchant/billing/gateways/cenpos.rb +62 -2
  15. data/lib/active_merchant/billing/gateways/checkout.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/checkout_v2.rb +3 -3
  17. data/lib/active_merchant/billing/gateways/conekta.rb +11 -1
  18. data/lib/active_merchant/billing/gateways/creditcall.rb +208 -0
  19. data/lib/active_merchant/billing/gateways/epay.rb +12 -0
  20. data/lib/active_merchant/billing/gateways/eway.rb +11 -0
  21. data/lib/active_merchant/billing/gateways/eway_rapid.rb +11 -0
  22. data/lib/active_merchant/billing/gateways/forte.rb +238 -0
  23. data/lib/active_merchant/billing/gateways/iridium.rb +11 -0
  24. data/lib/active_merchant/billing/gateways/jetpay.rb +10 -1
  25. data/lib/active_merchant/billing/gateways/linkpoint.rb +11 -0
  26. data/lib/active_merchant/billing/gateways/litle.rb +8 -1
  27. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/micropayment.rb +167 -0
  29. data/lib/active_merchant/billing/gateways/migs.rb +13 -4
  30. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  31. data/lib/active_merchant/billing/gateways/netbilling.rb +11 -1
  32. data/lib/active_merchant/billing/gateways/nmi.rb +164 -178
  33. data/lib/active_merchant/billing/gateways/ogone.rb +50 -15
  34. data/lib/active_merchant/billing/gateways/openpay.rb +10 -1
  35. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +5 -5
  36. data/lib/active_merchant/billing/gateways/payment_express.rb +11 -0
  37. data/lib/active_merchant/billing/gateways/paymill.rb +11 -0
  38. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  39. data/lib/active_merchant/billing/gateways/paystation.rb +3 -4
  40. data/lib/active_merchant/billing/gateways/pin.rb +10 -0
  41. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +106 -57
  42. data/lib/active_merchant/billing/gateways/realex.rb +10 -0
  43. data/lib/active_merchant/billing/gateways/redsys.rb +20 -0
  44. data/lib/active_merchant/billing/gateways/s5.rb +1 -0
  45. data/lib/active_merchant/billing/gateways/sage_pay.rb +11 -0
  46. data/lib/active_merchant/billing/gateways/secure_net.rb +1 -0
  47. data/lib/active_merchant/billing/gateways/stripe.rb +10 -4
  48. data/lib/active_merchant/billing/gateways/tns.rb +15 -4
  49. data/lib/active_merchant/billing/gateways/trust_commerce.rb +11 -0
  50. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +12 -2
  51. data/lib/active_merchant/billing/gateways/wirecard.rb +11 -1
  52. data/lib/active_merchant/billing/gateways/worldpay.rb +11 -0
  53. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +1 -1
  54. data/lib/active_merchant/version.rb +1 -1
  55. metadata +5 -2
@@ -151,12 +151,13 @@ module ActiveMerchant #:nodoc:
151
151
  # Verify and reserve the specified amount on the account, without actually doing the transaction.
152
152
  def authorize(money, payment_source, options = {})
153
153
  post = {}
154
+ action = (payment_source.brand == "mastercard") ? "PAU" : "RES"
154
155
  add_invoice(post, options)
155
156
  add_payment_source(post, payment_source, options)
156
157
  add_address(post, payment_source, options)
157
158
  add_customer_data(post, options)
158
159
  add_money(post, money, options)
159
- commit('RES', post)
160
+ commit(action, post)
160
161
  end
161
162
 
162
163
  # Verify and transfer the specified amount.
@@ -213,6 +214,18 @@ module ActiveMerchant #:nodoc:
213
214
  response
214
215
  end
215
216
 
217
+ def supports_scrubbing?
218
+ true
219
+ end
220
+
221
+ def scrub(transcript)
222
+ transcript.
223
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
224
+ gsub(%r((&?cardno=)[^&]*)i, '\1[FILTERED]').
225
+ gsub(%r((&?cvc=)[^&]*)i, '\1[FILTERED]').
226
+ gsub(%r((&?pswd=)[^&]*)i, '\1[FILTERED]')
227
+ end
228
+
216
229
  private
217
230
 
218
231
  def reference_from(authorization)
@@ -272,6 +285,8 @@ module ActiveMerchant #:nodoc:
272
285
  add_pair post, 'ACCEPTURL', options[:accept_url] if options[:accept_url]
273
286
  add_pair post, 'DECLINEURL', options[:decline_url] if options[:decline_url]
274
287
  add_pair post, 'EXCEPTIONURL', options[:exception_url] if options[:exception_url]
288
+ add_pair post, 'CANCELURL', options[:cancel_url] if options[:cancel_url]
289
+ add_pair post, 'PARAMVAR', options[:paramvar] if options[:paramvar]
275
290
  add_pair post, 'PARAMPLUS', options[:paramplus] if options[:paramplus]
276
291
  add_pair post, 'COMPLUS', options[:complus] if options[:complus]
277
292
  add_pair post, 'LANGUAGE', options[:language] if options[:language]
@@ -391,23 +406,43 @@ module ActiveMerchant #:nodoc:
391
406
  return
392
407
  end
393
408
 
394
- sha_encryptor = case @options[:signature_encryptor]
395
- when 'sha256'
396
- Digest::SHA256
397
- when 'sha512'
398
- Digest::SHA512
399
- else
400
- Digest::SHA1
401
- end
402
-
403
- string_to_digest = if @options[:signature_encryptor]
404
- parameters.sort { |a, b| a[0].upcase <=> b[0].upcase }.map { |k, v| "#{k.upcase}=#{v}" }.join(@options[:signature])
409
+ add_pair parameters, 'SHASign', calculate_signature(parameters, @options[:signature_encryptor], @options[:signature])
410
+ end
411
+
412
+ def calculate_signature(signed_parameters, algorithm, secret)
413
+ return legacy_calculate_signature(signed_parameters, secret) unless algorithm
414
+
415
+ sha_encryptor = case algorithm
416
+ when 'sha256'
417
+ Digest::SHA256
418
+ when 'sha512'
419
+ Digest::SHA512
420
+ when 'sha1'
421
+ Digest::SHA1
405
422
  else
406
- %w[orderID amount currency CARDNO PSPID Operation ALIAS].map { |key| parameters[key] }.join
423
+ raise "Unknown signature algorithm #{algorithm}"
407
424
  end
408
- string_to_digest << @options[:signature]
409
425
 
410
- add_pair parameters, 'SHASign', sha_encryptor.hexdigest(string_to_digest).upcase
426
+ sha_encryptor.hexdigest(
427
+ signed_parameters.sort_by{|k,v| k.upcase}.map{|k, v| "#{k.upcase}=#{v}#{secret}"}.join("")
428
+ ).upcase
429
+ end
430
+
431
+ def legacy_calculate_signature(parameters, secret)
432
+ Digest::SHA1.hexdigest(
433
+ (
434
+ %w(
435
+ orderID
436
+ amount
437
+ currency
438
+ CARDNO
439
+ PSPID
440
+ Operation
441
+ ALIAS
442
+ ).map{|key| parameters[key]} +
443
+ [secret]
444
+ ).join("")
445
+ ).upcase
411
446
  end
412
447
 
413
448
  def add_pair(post, key, value)
@@ -83,6 +83,16 @@ module ActiveMerchant #:nodoc:
83
83
  end
84
84
  end
85
85
 
86
+ def supports_scrubbing
87
+ true
88
+ end
89
+
90
+ def scrub(transcript)
91
+ transcript.
92
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
93
+ gsub(%r((card_number\\?":\\?")[^"\\]*)i, '\1[FILTERED]').
94
+ gsub(%r((cvv2\\?":\\?")[^"\\]*)i, '\1[FILTERED]')
95
+ end
86
96
  private
87
97
 
88
98
  def create_post_for_auth_or_purchase(money, creditcard, options)
@@ -191,4 +201,3 @@ module ActiveMerchant #:nodoc:
191
201
  end
192
202
  end
193
203
  end
194
-
@@ -25,15 +25,15 @@ module ActiveMerchant #:nodoc:
25
25
  'cvv2_not_checked' => 'X'
26
26
  }
27
27
 
28
- self.test_url = 'https://demo.deepcovelabs.com/realtime/'
29
- self.live_url = 'https://raven.pacnetservices.com/realtime/'
28
+ self.test_url = 'https://raven.deepcovelabs.com/realtime/'
29
+ self.live_url = 'https://raven.deepcovelabs.com/realtime/'
30
30
 
31
31
  self.supported_countries = ['US']
32
32
  self.supported_cardtypes = [:visa, :master]
33
33
  self.money_format = :cents
34
34
  self.default_currency = 'USD'
35
- self.homepage_url = 'http://www.pacnetservices.com/'
36
- self.display_name = 'Raven PacNet'
35
+ self.homepage_url = 'http://www.deepcovelabs.com/raven'
36
+ self.display_name = 'Raven'
37
37
 
38
38
  def initialize(options = {})
39
39
  requires!(options, :user, :secret, :prn)
@@ -63,7 +63,7 @@ module ActiveMerchant #:nodoc:
63
63
  def void(authorization, options = {})
64
64
  post = {}
65
65
  post['TrackingNumber'] = authorization
66
- post['PymtType'] = options[:pymt_type] || 'cc_debit'
66
+ post['PymtType'] = options[:pymt_type]
67
67
 
68
68
  commit('void', nil, post)
69
69
  end
@@ -122,6 +122,17 @@ module ActiveMerchant #:nodoc:
122
122
  commit(:validate, request)
123
123
  end
124
124
 
125
+ def supports_scrubbing
126
+ true
127
+ end
128
+
129
+ def scrub(transcript)
130
+ transcript.
131
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
132
+ gsub(%r((<CardNumber>)\d+(</CardNumber>)), '\1[FILTERED]\2').
133
+ gsub(%r((<Cvc2>)\d+(</Cvc2>)), '\1[FILTERED]\2')
134
+ end
135
+
125
136
  private
126
137
 
127
138
  def use_custom_payment_token?
@@ -51,6 +51,17 @@ module ActiveMerchant #:nodoc:
51
51
  save_card(credit_card)
52
52
  end
53
53
 
54
+ def supports_scrubbing
55
+ true
56
+ end
57
+
58
+ def scrub(transcript)
59
+ transcript.
60
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
61
+ gsub(/(account.number=)(\d*)/, '\1[FILTERED]').
62
+ gsub(/(account.verification=)(\d*)/, '\1[FILTERED]')
63
+ end
64
+
54
65
  private
55
66
 
56
67
  def add_credit_card(post, credit_card)
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  module PaypalCommonAPI
5
5
  include Empty
6
6
 
7
- API_VERSION = '72'
7
+ API_VERSION = '124'
8
8
 
9
9
  URLS = {
10
10
  :test => { :certificate => 'https://api.sandbox.paypal.com/2.0/',
@@ -102,9 +102,9 @@ module ActiveMerchant #:nodoc:
102
102
  end
103
103
 
104
104
  def add_invoice(post, options)
105
- post[:ms] = options[:order_id] || generate_unique_id
106
- post[:mo] = options[:invoice]
107
- post[:mr] = options[:description]
105
+ post[:ms] = generate_unique_id
106
+ post[:mo] = options[:description]
107
+ post[:mr] = options[:order_id]
108
108
  end
109
109
 
110
110
  def add_credit_card(post, credit_card)
@@ -192,4 +192,3 @@ module ActiveMerchant #:nodoc:
192
192
  end
193
193
  end
194
194
  end
195
-
@@ -75,6 +75,16 @@ module ActiveMerchant #:nodoc:
75
75
  commit(:put, "customers/#{CGI.escape(token)}", post, options)
76
76
  end
77
77
 
78
+ def supports_scrubbing
79
+ true
80
+ end
81
+
82
+ def scrub(transcript)
83
+ transcript.
84
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
85
+ gsub(/(number\\?":\\?")(\d*)/, '\1[FILTERED]').
86
+ gsub(/(cvc\\?":\\?")(\d*)/, '\1[FILTERED]')
87
+ end
78
88
  private
79
89
 
80
90
  def add_amount(post, money, options)
@@ -6,25 +6,29 @@ module ActiveMerchant
6
6
  class QuickpayV10Gateway < Gateway
7
7
  include QuickpayCommon
8
8
  API_VERSION = 10
9
-
9
+
10
10
  self.live_url = self.test_url = 'https://api.quickpay.net'
11
-
11
+
12
12
  def initialize(options = {})
13
13
  requires!(options, :api_key)
14
14
  super
15
15
  end
16
-
16
+
17
17
  def purchase(money, credit_card, options = {})
18
18
  MultiResponse.run(true) do |r|
19
19
  r.process { create_payment(money, options) }
20
20
  r.process {
21
21
  post = authorization_params(money, credit_card, options)
22
- add_autocapture(post, true)
22
+ add_autocapture(post, false)
23
23
  commit(synchronized_path("/payments/#{r.authorization}/authorize"), post)
24
24
  }
25
+ r.process {
26
+ post = capture_params(money, credit_card, options)
27
+ commit(synchronized_path("/payments/#{r.authorization}/capture"), post)
28
+ }
25
29
  end
26
30
  end
27
-
31
+
28
32
  def authorize(money, credit_card, options = {})
29
33
  MultiResponse.run(true) do |r|
30
34
  r.process { create_payment(money, options) }
@@ -34,8 +38,8 @@ module ActiveMerchant
34
38
  }
35
39
  end
36
40
  end
37
-
38
- def void(identification)
41
+
42
+ def void(identification, _options = {})
39
43
  commit(synchronized_path "/payments/#{identification}/cancel")
40
44
  end
41
45
 
@@ -45,9 +49,7 @@ module ActiveMerchant
45
49
  end
46
50
 
47
51
  def capture(money, identification, options = {})
48
- post = {}
49
- add_amount(post, money, options)
50
- add_additional_params(:capture, post, options)
52
+ post = capture_params(money, identification, options)
51
53
  commit(synchronized_path("/payments/#{identification}/capture"), post)
52
54
  end
53
55
 
@@ -57,47 +59,78 @@ module ActiveMerchant
57
59
  add_additional_params(:refund, post, options)
58
60
  commit(synchronized_path("/payments/#{identification}/refund"), post)
59
61
  end
60
-
62
+
63
+ def verify(credit_card, options={})
64
+ MultiResponse.run(:use_first_response) do |r|
65
+ r.process { authorize(100, credit_card, options) }
66
+ r.process(:ignore_result) { void(r.authorization, options) }
67
+ end
68
+ end
69
+
61
70
  def store(credit_card, options = {})
62
- MultiResponse.run(true) do |r|
71
+ MultiResponse.run(true) do |r|
63
72
  r.process { create_subscription(options) }
64
- r.process {
65
- authorize_subscription(r.authorization, credit_card, options)
73
+ r.process {
74
+ authorize_subscription(r.authorization, credit_card, options)
66
75
  }
67
76
  end
68
77
  end
69
-
78
+
70
79
  def unstore(identification)
71
80
  commit(synchronized_path "/subscriptions/#{identification}/cancel")
72
81
  end
73
-
82
+
83
+ def supports_scrubbing?
84
+ true
85
+ end
86
+
87
+ def scrub(transcript)
88
+ transcript.
89
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
90
+ gsub(%r(("card\\?":{\\?"number\\?":\\?")\d+), '\1[FILTERED]').
91
+ gsub(%r(("cvd\\?":\\?")\d+), '\1[FILTERED]')
92
+ end
93
+
74
94
  private
75
-
95
+
76
96
  def authorization_params(money, credit_card, options = {})
77
97
  post = {}
78
-
98
+
79
99
  add_amount(post, money, options)
80
100
  add_credit_card(post, credit_card)
81
101
  add_additional_params(:authorize, post, options)
82
-
102
+
103
+ post
104
+ end
105
+
106
+ def capture_params(money, credit_card, options = {})
107
+ post = {}
108
+
109
+ add_amount(post, money, options)
110
+ add_additional_params(:capture, post, options)
111
+
83
112
  post
84
113
  end
85
-
114
+
86
115
  def create_subscription(options = {})
87
- post = {}
88
-
116
+ requires!(options, :currency)
117
+ post = {}
118
+
119
+ add_currency(post, nil, options)
89
120
  add_subscription_invoice(post, options)
90
121
  commit('/subscriptions', post)
91
122
  end
92
-
123
+
93
124
  def authorize_subscription(identification, credit_card, options = {})
125
+ requires!(options, :amount)
94
126
  post = {}
95
-
127
+
128
+ add_amount(post, nil, options)
96
129
  add_credit_card(post, credit_card, options)
97
130
  add_additional_params(:authorize_subscription, post, options)
98
131
  commit(synchronized_path("/subscriptions/#{identification}/authorize"), post)
99
132
  end
100
-
133
+
101
134
  def create_payment(money, options = {})
102
135
  post = {}
103
136
  add_currency(post, money, options)
@@ -106,7 +139,7 @@ module ActiveMerchant
106
139
  end
107
140
 
108
141
  def commit(action, params = {})
109
- success = false
142
+ success = false
110
143
  begin
111
144
  response = parse(ssl_post(self.live_url + action, params.to_json, headers))
112
145
  success = successful?(response)
@@ -115,59 +148,59 @@ module ActiveMerchant
115
148
  rescue JSON::ParserError
116
149
  response = json_error(response)
117
150
  end
118
-
151
+
119
152
  Response.new(success, message_from(success, response), response,
120
153
  :test => test?,
121
154
  :authorization => response['id']
122
155
  )
123
156
  end
124
-
157
+
125
158
  def add_subscription_invoice(post, options = {})
126
- requires!(options, :order_id, :description)
159
+ requires!(options, :order_id, :description)
127
160
  post[:order_id] = options[:order_id]
128
161
  post[:description] = options[:description]
129
162
  end
130
-
163
+
131
164
  def add_currency(post, money, options)
132
165
  post[:currency] = options[:currency] || currency(money)
133
166
  end
134
-
167
+
135
168
  def add_amount(post, money, options)
136
- post[:amount] = amount(money)
169
+ post[:amount] = options[:amount] || amount(money)
137
170
  end
138
-
171
+
139
172
  def add_autocapture(post, value)
140
- post[:auto_capture] = value
173
+ post[:auto_capture] = value
141
174
  end
142
-
175
+
143
176
  def add_order_id(post, options)
144
- requires!(options, :order_id)
145
- post[:order_id] = options[:order_id]
177
+ requires!(options, :order_id)
178
+ post[:order_id] = format_order_id(options[:order_id])
146
179
  end
147
-
180
+
148
181
  def add_invoice(post, options)
149
- add_order_id(post, options)
150
-
182
+ add_order_id(post, options)
183
+
151
184
  if options[:billing_address]
152
185
  post[:invoice_address] = map_address(options[:billing_address])
153
186
  end
154
-
187
+
155
188
  if options[:shipping_address]
156
189
  post[:shipping_address] = map_address(options[:shipping_address])
157
190
  end
158
-
191
+
159
192
  [:metadata, :brading_id, :variables].each do |field|
160
193
  post[field] = options[field] if options[field]
161
194
  end
162
195
  end
163
-
196
+
164
197
  def add_additional_params(action, post, options = {})
165
198
  MD5_CHECK_FIELDS[API_VERSION][action].each do |key|
166
199
  key = key.to_sym
167
200
  post[key] = options[key] if options[key]
168
201
  end
169
202
  end
170
-
203
+
171
204
  def add_credit_card(post, credit_card, options = {})
172
205
  post[:card] ||= {}
173
206
  post[:card][:number] = credit_card.number
@@ -175,36 +208,52 @@ module ActiveMerchant
175
208
  post[:card][:expiration] = expdate(credit_card)
176
209
  post[:card][:issued_to] = credit_card.name
177
210
  end
178
-
211
+
179
212
  def parse(body)
180
213
  JSON.parse(body)
181
214
  end
182
-
215
+
183
216
  def successful?(response)
184
217
  has_error = response['errors']
185
- invalid_code = (response.key?('qp_status_code') and response['qp_status_code'] != "20000")
186
-
218
+ invalid_code = invalid_operation_code?(response)
219
+
187
220
  !(has_error || invalid_code)
188
221
  end
189
-
222
+
190
223
  def message_from(success, response)
191
- success ? 'OK' : (response['message'] || response['qp_status_msg'])
224
+ success ? 'OK' : (response['message'] || invalid_operation_message(response) || "Unknown error - please contact QuickPay")
225
+ end
226
+
227
+ def invalid_operation_code?(response)
228
+ if response['operations']
229
+ operation = response['operations'].last
230
+ operation && operation['qp_status_code'] != "20000"
231
+ end
232
+ end
233
+
234
+ def invalid_operation_message(response)
235
+ response['operations'] && response['operations'].last['qp_status_msg']
192
236
  end
193
-
237
+
194
238
  def map_address(address)
195
239
  return {} if address.nil?
196
240
  requires!(address, :name, :address1, :city, :zip, :country)
241
+ country = Country.find(address[:country])
197
242
  mapped = {
198
243
  :name => address[:name],
199
244
  :street => address[:address1],
200
245
  :city => address[:city],
201
246
  :region => address[:address2],
202
247
  :zip_code => address[:zip],
203
- :country_code => address[:country]
248
+ :country_code => country.code(:alpha3).value
204
249
  }
205
250
  mapped
206
251
  end
207
252
 
253
+ def format_order_id(order_id)
254
+ order_id.to_s.gsub(/#/, '')
255
+ end
256
+
208
257
  def headers
209
258
  auth = Base64.strict_encode64(":#{@options[:api_key]}")
210
259
  {
@@ -215,7 +264,7 @@ module ActiveMerchant
215
264
  "Content-Type" => "application/json"
216
265
  }
217
266
  end
218
-
267
+
219
268
  def response_error(raw_response)
220
269
  begin
221
270
  parse(raw_response)
@@ -229,12 +278,12 @@ module ActiveMerchant
229
278
  msg += " (The raw response returned by the API was #{raw_response.inspect})"
230
279
  { "message" => msg }
231
280
  end
232
-
281
+
233
282
  def synchronized_path(path)
234
283
  "#{path}?synchronized"
235
284
  end
236
-
285
+
237
286
  end
238
-
287
+
239
288
  end
240
- end
289
+ end