activemerchant 1.90.0 → 1.91.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 +38 -0
  3. data/README.md +3 -2
  4. data/lib/active_merchant/billing/credit_card_methods.rb +1 -1
  5. data/lib/active_merchant/billing/gateways/adyen.rb +26 -21
  6. data/lib/active_merchant/billing/gateways/bambora_apac.rb +186 -0
  7. data/lib/active_merchant/billing/gateways/blue_snap.rb +189 -34
  8. data/lib/active_merchant/billing/gateways/braintree_blue.rb +11 -9
  9. data/lib/active_merchant/billing/gateways/card_connect.rb +3 -0
  10. data/lib/active_merchant/billing/gateways/cecabank.rb +13 -2
  11. data/lib/active_merchant/billing/gateways/fat_zebra.rb +20 -7
  12. data/lib/active_merchant/billing/gateways/ipp.rb +1 -0
  13. data/lib/active_merchant/billing/gateways/moneris.rb +3 -4
  14. data/lib/active_merchant/billing/gateways/netbanx.rb +4 -0
  15. data/lib/active_merchant/billing/gateways/nmi.rb +6 -4
  16. data/lib/active_merchant/billing/gateways/openpay.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/orbital.rb +8 -1
  18. data/lib/active_merchant/billing/gateways/payment_express.rb +4 -1
  19. data/lib/active_merchant/billing/gateways/paymentez.rb +4 -9
  20. data/lib/active_merchant/billing/gateways/pin.rb +19 -6
  21. data/lib/active_merchant/billing/gateways/pro_pay.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/qvalent.rb +11 -0
  23. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +3 -3
  24. data/lib/active_merchant/billing/gateways/trust_commerce.rb +1 -0
  25. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +11 -3
  26. data/lib/active_merchant/billing/gateways/worldpay.rb +51 -2
  27. data/lib/active_merchant/country.rb +1 -1
  28. data/lib/active_merchant/version.rb +1 -1
  29. metadata +3 -2
@@ -324,15 +324,15 @@ module ActiveMerchant #:nodoc:
324
324
  :region => address[:state],
325
325
  :postal_code => scrub_zip(address[:zip]),
326
326
  }
327
- if address[:country] || address[:country_code_alpha2]
328
- mapped[:country_code_alpha2] = (address[:country] || address[:country_code_alpha2])
329
- elsif address[:country_name]
330
- mapped[:country_name] = address[:country_name]
331
- elsif address[:country_code_alpha3]
332
- mapped[:country_code_alpha3] = address[:country_code_alpha3]
333
- elsif address[:country_code_numeric]
334
- mapped[:country_code_numeric] = address[:country_code_numeric]
327
+
328
+ mapped[:country_code_alpha2] = (address[:country] || address[:country_code_alpha2]) if address[:country] || address[:country_code_alpha2]
329
+ mapped[:country_name] = address[:country_name] if address[:country_name]
330
+ mapped[:country_code_alpha3] = address[:country_code_alpha3] if address[:country_code_alpha3]
331
+ unless address[:country].blank?
332
+ mapped[:country_code_alpha3] ||= Country.find(address[:country]).code(:alpha3).value
335
333
  end
334
+ mapped[:country_code_numeric] = address[:country_code_numeric] if address[:country_code_numeric]
335
+
336
336
  mapped
337
337
  end
338
338
 
@@ -585,7 +585,9 @@ module ActiveMerchant #:nodoc:
585
585
  parameters[:merchant_account_id] = merchant_account_id
586
586
  end
587
587
 
588
- if options[:recurring]
588
+ if options[:transaction_source]
589
+ parameters[:transaction_source] = options[:transaction_source]
590
+ elsif options[:recurring]
589
591
  parameters[:recurring] = true
590
592
  end
591
593
 
@@ -281,6 +281,9 @@ module ActiveMerchant #:nodoc:
281
281
  test: test?,
282
282
  error_code: error_code_from(response)
283
283
  )
284
+ rescue ResponseError => e
285
+ return Response.new(false, 'Unable to authenticate. Please check your credentials.', {}, :test => test?) if e.response.code == '401'
286
+ raise
284
287
  end
285
288
 
286
289
  def success_from(response)
@@ -173,13 +173,24 @@ module ActiveMerchant #:nodoc:
173
173
  response = parse(xml)
174
174
  Response.new(
175
175
  response[:success],
176
- response[:message],
176
+ message_from(response),
177
177
  response,
178
178
  :test => test?,
179
- :authorization => build_authorization(response)
179
+ :authorization => build_authorization(response),
180
+ :error_code => response[:error_code]
180
181
  )
181
182
  end
182
183
 
184
+ def message_from(response)
185
+ if response[:message] == 'ERROR' && response[:error_message]
186
+ response[:error_message]
187
+ elsif response[:error_message]
188
+ "#{response[:message]} #{response[:error_message]}"
189
+ else
190
+ response[:message]
191
+ end
192
+ end
193
+
183
194
  def post_data(params)
184
195
  return nil unless params
185
196
 
@@ -46,14 +46,17 @@ module ActiveMerchant #:nodoc:
46
46
  end
47
47
 
48
48
  def capture(money, authorization, options = {})
49
+ txn_id, _ = authorization.to_s.split('|')
49
50
  post = {}
51
+
50
52
  add_amount(post, money, options)
51
53
  add_extra_options(post, options)
52
54
 
53
- commit(:post, "purchases/#{CGI.escape(authorization)}/capture", post)
55
+ commit(:post, "purchases/#{CGI.escape(txn_id)}/capture", post)
54
56
  end
55
57
 
56
- def refund(money, txn_id, options={})
58
+ def refund(money, authorization, options={})
59
+ txn_id, _ = authorization.to_s.split('|')
57
60
  post = {}
58
61
 
59
62
  add_extra_options(post, options)
@@ -64,8 +67,15 @@ module ActiveMerchant #:nodoc:
64
67
  commit(:post, 'refunds', post)
65
68
  end
66
69
 
70
+ def void(authorization, options={})
71
+ txn_id, endpoint = authorization.to_s.split('|')
72
+
73
+ commit(:post, "#{endpoint}/void?id=#{txn_id}", {})
74
+ end
75
+
67
76
  def store(creditcard, options={})
68
77
  post = {}
78
+
69
79
  add_creditcard(post, creditcard)
70
80
 
71
81
  commit(:post, 'credit_cards', post)
@@ -97,7 +107,8 @@ module ActiveMerchant #:nodoc:
97
107
  post[:cvv] = creditcard.verification_value if creditcard.verification_value?
98
108
  post[:card_holder] = creditcard.name if creditcard.name
99
109
  elsif creditcard.is_a?(String)
100
- post[:card_token] = creditcard
110
+ id, _ = creditcard.to_s.split('|')
111
+ post[:card_token] = id
101
112
  post[:cvv] = options[:cvv]
102
113
  elsif creditcard.is_a?(Hash)
103
114
  ActiveMerchant.deprecated 'Passing the credit card as a Hash is deprecated. Use a String and put the (optional) CVV in the options hash instead.'
@@ -141,7 +152,7 @@ module ActiveMerchant #:nodoc:
141
152
  message_from(response),
142
153
  response,
143
154
  :test => response['test'],
144
- :authorization => authorization_from(response, success)
155
+ :authorization => authorization_from(response, success, uri)
145
156
  )
146
157
  end
147
158
 
@@ -149,13 +160,15 @@ module ActiveMerchant #:nodoc:
149
160
  (
150
161
  response['successful'] &&
151
162
  response['response'] &&
152
- (response['response']['successful'] || response['response']['token'])
163
+ (response['response']['successful'] || response['response']['token'] || response['response']['response_code'] == '00')
153
164
  )
154
165
  end
155
166
 
156
- def authorization_from(response, success)
167
+ def authorization_from(response, success, uri)
168
+ endpoint = uri.split('/')[0]
157
169
  if success
158
- (response['response']['id'] || response['response']['token'])
170
+ id = response['response']['id'] || response['response']['token']
171
+ "#{id}|#{endpoint}"
159
172
  else
160
173
  nil
161
174
  end
@@ -22,6 +22,7 @@ module ActiveMerchant #:nodoc:
22
22
  }
23
23
 
24
24
  def initialize(options={})
25
+ ActiveMerchant.deprecated('IPP gateway is now named Bambora Asia-Pacific')
25
26
  requires!(options, :username, :password)
26
27
  super
27
28
  end
@@ -33,7 +33,6 @@ module ActiveMerchant #:nodoc:
33
33
  requires!(options, :login, :password)
34
34
  @cvv_enabled = options[:cvv_enabled]
35
35
  @avs_enabled = options[:avs_enabled]
36
- @cof_enabled = options[:cof_enabled]
37
36
  options[:crypt_type] = 7 unless options.has_key?(:crypt_type)
38
37
  super
39
38
  end
@@ -51,7 +50,7 @@ module ActiveMerchant #:nodoc:
51
50
  post[:order_id] = options[:order_id]
52
51
  post[:address] = options[:billing_address] || options[:address]
53
52
  post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
54
- add_cof(post, options) if @cof_enabled
53
+ add_cof(post, options)
55
54
  action = if post[:cavv]
56
55
  'cavv_preauth'
57
56
  elsif post[:data_key].blank?
@@ -74,7 +73,7 @@ module ActiveMerchant #:nodoc:
74
73
  post[:order_id] = options[:order_id]
75
74
  post[:address] = options[:billing_address] || options[:address]
76
75
  post[:crypt_type] = options[:crypt_type] || @options[:crypt_type]
77
- add_cof(post, options) if @cof_enabled
76
+ add_cof(post, options)
78
77
  action = if post[:cavv]
79
78
  'cavv_purchase'
80
79
  elsif post[:data_key].blank?
@@ -293,7 +292,7 @@ module ActiveMerchant #:nodoc:
293
292
  when :cvd_info
294
293
  transaction.add_element(cvd_element(parameters[:cvd_value])) if @cvv_enabled
295
294
  when :cof_info
296
- transaction.add_element(credential_on_file(parameters)) if @cof_enabled && cof_details_present?(parameters)
295
+ transaction.add_element(credential_on_file(parameters)) if cof_details_present?(parameters)
297
296
  else
298
297
  transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank?
299
298
  end
@@ -55,6 +55,10 @@ module ActiveMerchant #:nodoc:
55
55
  post = {}
56
56
  add_invoice(post, money, options)
57
57
 
58
+ # Setting merchantRefNumber to a unique id for each refund
59
+ # This is to support multiple partial refunds for the same order
60
+ post[:merchantRefNum] = SecureRandom.uuid
61
+
58
62
  commit(:post, "settlements/#{authorization}/refunds", post)
59
63
  end
60
64
 
@@ -144,7 +144,8 @@ module ActiveMerchant #:nodoc:
144
144
 
145
145
  def add_payment_method(post, payment_method, options)
146
146
  if(payment_method.is_a?(String))
147
- post[:customer_vault_id] = payment_method
147
+ customer_vault_id, _ = split_authorization(payment_method)
148
+ post[:customer_vault_id] = customer_vault_id
148
149
  elsif payment_method.is_a?(NetworkTokenizationCreditCard)
149
150
  post[:ccnumber] = payment_method.number
150
151
  post[:ccexp] = exp_date(payment_method)
@@ -236,15 +237,16 @@ module ActiveMerchant #:nodoc:
236
237
  succeeded,
237
238
  message_from(succeeded, response),
238
239
  response,
239
- authorization: authorization_from(response, params[:payment]),
240
+ authorization: authorization_from(response, params[:payment], action),
240
241
  avs_result: AVSResult.new(code: response[:avsresponse]),
241
242
  cvv_result: CVVResult.new(response[:cvvresponse]),
242
243
  test: test?
243
244
  )
244
245
  end
245
246
 
246
- def authorization_from(response, payment_type)
247
- [ response[:transactionid], payment_type ].join('#')
247
+ def authorization_from(response, payment_type, action)
248
+ authorization = (action == 'add_customer' ? response[:customer_vault_id] : response[:transactionid])
249
+ [ authorization, payment_type ].join('#')
248
250
  end
249
251
 
250
252
  def split_authorization(authorization)
@@ -205,7 +205,7 @@ module ActiveMerchant #:nodoc:
205
205
  end
206
206
 
207
207
  def error?(response)
208
- response.key?('error_code')
208
+ response['error_code'] && !response['error_code'].blank?
209
209
  end
210
210
 
211
211
  def response_error(raw_response)
@@ -30,7 +30,7 @@ module ActiveMerchant #:nodoc:
30
30
  class OrbitalGateway < Gateway
31
31
  include Empty
32
32
 
33
- API_VERSION = '7.1'
33
+ API_VERSION = '7.7'
34
34
 
35
35
  POST_HEADERS = {
36
36
  'MIME-Version' => '1.1',
@@ -506,6 +506,12 @@ module ActiveMerchant #:nodoc:
506
506
  end
507
507
  end
508
508
 
509
+ def add_stored_credentials(xml, parameters)
510
+ xml.tag! :MITMsgType, parameters[:mit_msg_type] if parameters[:mit_msg_type]
511
+ xml.tag! :MITStoredCredentialInd, parameters[:mit_stored_credential_ind] if parameters[:mit_stored_credential_ind]
512
+ xml.tag! :MITSubmittedTransactionID, parameters[:mit_submitted_transaction_id] if parameters[:mit_submitted_transaction_id]
513
+ end
514
+
509
515
  def parse(body)
510
516
  response = {}
511
517
  xml = REXML::Document.new(body)
@@ -635,6 +641,7 @@ module ActiveMerchant #:nodoc:
635
641
  end
636
642
 
637
643
  add_level_2_purchase(xml, parameters)
644
+ add_stored_credentials(xml, parameters)
638
645
  end
639
646
  end
640
647
  xml.target!
@@ -241,7 +241,8 @@ module ActiveMerchant #:nodoc:
241
241
  # :client_type => :web, # Possible values are: :web, :ivr, :moto, :unattended, :internet, or :recurring
242
242
  # :txn_data1 => "String up to 255 characters",
243
243
  # :txn_data2 => "String up to 255 characters",
244
- # :txn_data3 => "String up to 255 characters"
244
+ # :txn_data3 => "String up to 255 characters",
245
+ # :client_info => "String up to 15 characters. The IP address of the user who processed the transaction."
245
246
  # }
246
247
  #
247
248
  # +:client_type+, while not documented for PxPost, will be sent as
@@ -277,6 +278,8 @@ module ActiveMerchant #:nodoc:
277
278
  xml.add_element('TxnData1').text = options[:txn_data1].to_s.slice(0, 255) unless options[:txn_data1].blank?
278
279
  xml.add_element('TxnData2').text = options[:txn_data2].to_s.slice(0, 255) unless options[:txn_data2].blank?
279
280
  xml.add_element('TxnData3').text = options[:txn_data3].to_s.slice(0, 255) unless options[:txn_data3].blank?
281
+
282
+ xml.add_element('ClientInfo').text = options[:client_info] if options[:client_info]
280
283
  end
281
284
 
282
285
  def new_transaction
@@ -171,15 +171,10 @@ module ActiveMerchant #:nodoc:
171
171
  end
172
172
 
173
173
  def add_extra_params(post, options)
174
- if options[:extra_params]
175
- items = {}
176
- options[:extra_params].each do |param|
177
- param.each do |key, value|
178
- items[key.to_sym] = value
179
- end
180
- end
181
- post[:extra_params] = items
182
- end
174
+ extra_params = {}
175
+ extra_params.merge!(options[:extra_params]) if options[:extra_params]
176
+
177
+ post['extra_params'] = extra_params unless extra_params.empty?
183
178
  end
184
179
 
185
180
  def parse(body)
@@ -1,14 +1,14 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class PinGateway < Gateway
4
- self.test_url = 'https://test-api.pin.net.au/1'
5
- self.live_url = 'https://api.pin.net.au/1'
4
+ self.test_url = 'https://test-api.pinpayments.com/1'
5
+ self.live_url = 'https://api.pinpayments.com/1'
6
6
 
7
7
  self.default_currency = 'AUD'
8
8
  self.money_format = :cents
9
9
  self.supported_countries = ['AU']
10
10
  self.supported_cardtypes = [:visa, :master, :american_express]
11
- self.homepage_url = 'http://www.pin.net.au/'
11
+ self.homepage_url = 'http://www.pinpayments.com/'
12
12
  self.display_name = 'Pin Payments'
13
13
 
14
14
  def initialize(options = {})
@@ -67,6 +67,7 @@ module ActiveMerchant #:nodoc:
67
67
  # Updates the credit card for the customer.
68
68
  def update(token, creditcard, options = {})
69
69
  post = {}
70
+ token = get_customer_token(token)
70
71
 
71
72
  add_creditcard(post, creditcard)
72
73
  add_customer_data(post, options)
@@ -137,13 +138,21 @@ module ActiveMerchant #:nodoc:
137
138
  )
138
139
  elsif creditcard.kind_of?(String)
139
140
  if creditcard =~ /^card_/
140
- post[:card_token] = creditcard
141
+ post[:card_token] = get_card_token(creditcard)
141
142
  else
142
143
  post[:customer_token] = creditcard
143
144
  end
144
145
  end
145
146
  end
146
147
 
148
+ def get_customer_token(token)
149
+ token.split(/;(?=cus)/).last
150
+ end
151
+
152
+ def get_card_token(token)
153
+ token.split(/;(?=cus)/).first
154
+ end
155
+
147
156
  def add_metadata(post, options)
148
157
  post[:metadata] = options[:metadata] if options[:metadata]
149
158
  end
@@ -200,13 +209,17 @@ module ActiveMerchant #:nodoc:
200
209
  end
201
210
 
202
211
  def unparsable_response(raw_response)
203
- message = 'Invalid JSON response received from Pin Payments. Please contact support@pin.net.au if you continue to receive this message.'
212
+ message = 'Invalid JSON response received from Pin Payments. Please contact support@pinpayments.com if you continue to receive this message.'
204
213
  message += " (The raw response returned by the API was #{raw_response.inspect})"
205
214
  return Response.new(false, message)
206
215
  end
207
216
 
208
217
  def token(response)
209
- response['token']
218
+ if response['token'].start_with?('cus')
219
+ "#{response.dig('card', 'token')};#{response['token']}"
220
+ else
221
+ response['token']
222
+ end
210
223
  end
211
224
 
212
225
  def parse(body)
@@ -234,7 +234,7 @@ module ActiveMerchant #:nodoc:
234
234
  xml.aptNum address[:address2]
235
235
  xml.city address[:city]
236
236
  xml.state address[:state]
237
- xml.zip address[:zip]
237
+ xml.zip address[:zip].to_s.delete('-')
238
238
  end
239
239
  end
240
240
 
@@ -12,6 +12,10 @@ module ActiveMerchant #:nodoc:
12
12
  self.money_format = :cents
13
13
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners]
14
14
 
15
+ CVV_CODE_MAPPING = {
16
+ 'S' => 'D'
17
+ }
18
+
15
19
  def initialize(options={})
16
20
  requires!(options, :username, :password, :merchant, :pem, :pem_password)
17
21
  super
@@ -168,11 +172,18 @@ module ActiveMerchant #:nodoc:
168
172
  message_from(succeeded, raw),
169
173
  raw,
170
174
  authorization: raw['response.orderNumber'] || raw['response.customerReferenceNumber'],
175
+ cvv_result: cvv_result(succeeded, raw),
171
176
  error_code: error_code_from(succeeded, raw),
172
177
  test: test?
173
178
  )
174
179
  end
175
180
 
181
+ def cvv_result(succeeded, raw)
182
+ return unless succeeded
183
+ code = CVV_CODE_MAPPING[raw['response.cvnResponse']] || raw['response.cvnResponse']
184
+ CVVResult.new(code)
185
+ end
186
+
176
187
  def headers
177
188
  {
178
189
  'Content-Type' => 'application/x-www-form-urlencoded'
@@ -532,7 +532,7 @@ module ActiveMerchant #:nodoc:
532
532
 
533
533
  def add_contact(doc, fullname, options)
534
534
  doc['v1'].contact do
535
- doc['v1'].fullName fullname
535
+ doc['v1'].fullName fullname unless fullname.blank?
536
536
  doc['v1'].coName options[:company_name] if options[:company_name]
537
537
  doc['v1'].title options[:title] if options[:title]
538
538
 
@@ -557,7 +557,7 @@ module ActiveMerchant #:nodoc:
557
557
 
558
558
  if (shipping_address = options[:shipping_address])
559
559
  doc['v1'].ship do
560
- doc['v1'].fullName fullname
560
+ doc['v1'].fullName fullname unless fullname.blank?
561
561
  doc['v1'].addrLn1 shipping_address[:address1] if shipping_address[:address1]
562
562
  doc['v1'].addrLn2 shipping_address[:address2] if shipping_address[:address2]
563
563
  doc['v1'].city shipping_address[:city] if shipping_address[:city]
@@ -572,7 +572,7 @@ module ActiveMerchant #:nodoc:
572
572
 
573
573
  def add_name(doc, payment_method)
574
574
  doc['v1'].contact do
575
- doc['v1'].fullName payment_method.name
575
+ doc['v1'].fullName payment_method.name unless payment_method.name.blank?
576
576
  end
577
577
  end
578
578
 
@@ -333,6 +333,7 @@ module ActiveMerchant #:nodoc:
333
333
  params[:routing] = check.routing_number
334
334
  params[:account] = check.account_number
335
335
  params[:savings] = 'y' if check.account_type == 'savings'
336
+ params[:name] = check.name
336
337
  end
337
338
 
338
339
  def add_creditcard(params, creditcard)