activemerchant 1.95.0 → 1.96.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +45 -0
  3. data/README.md +3 -0
  4. data/lib/active_merchant/billing/avs_result.rb +4 -5
  5. data/lib/active_merchant/billing/credit_card.rb +2 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +14 -0
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +19 -6
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +39 -10
  10. data/lib/active_merchant/billing/gateways/blue_snap.rb +4 -1
  11. data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
  12. data/lib/active_merchant/billing/gateways/braintree_blue.rb +11 -1
  13. data/lib/active_merchant/billing/gateways/card_connect.rb +1 -0
  14. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
  15. data/lib/active_merchant/billing/gateways/checkout_v2.rb +24 -24
  16. data/lib/active_merchant/billing/gateways/credorax.rb +29 -3
  17. data/lib/active_merchant/billing/gateways/cyber_source.rb +2 -2
  18. data/lib/active_merchant/billing/gateways/decidir.rb +232 -0
  19. data/lib/active_merchant/billing/gateways/global_collect.rb +2 -6
  20. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  21. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/migs.rb +8 -0
  23. data/lib/active_merchant/billing/gateways/mundipagg.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  25. data/lib/active_merchant/billing/gateways/nmi.rb +39 -1
  26. data/lib/active_merchant/billing/gateways/opp.rb +20 -1
  27. data/lib/active_merchant/billing/gateways/payflow.rb +40 -2
  28. data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
  29. data/lib/active_merchant/billing/gateways/realex.rb +11 -5
  30. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  31. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
  32. data/lib/active_merchant/billing/gateways/trust_commerce.rb +24 -5
  33. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
  34. data/lib/active_merchant/billing/gateways/worldpay.rb +157 -37
  35. data/lib/active_merchant/country.rb +1 -0
  36. data/lib/active_merchant/version.rb +1 -1
  37. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0af5c8a759df2f80d48246fe7d1c71bff144e2e4916ccf47aad3a96f79820f79
4
- data.tar.gz: 5f2865f068ee003ea07f304f64894800ed870f32020b71f73c06e3d422508f1c
3
+ metadata.gz: 7e6a20c62ca88a52c155c0d8f3f33e7a6469e93b8bfb1734dcca624d65277143
4
+ data.tar.gz: b715b562b0d1d59cc77379fd288ee88fe7756df860f2d8e28337b023ecf46f30
5
5
  SHA512:
6
- metadata.gz: 7b341fa74b52b79347fc60ab22e3953616ff0cba341f1564b39f0174c3821d241ae7d63e5965151d73d16d13a1fb40a2175c01b6b27a2d32355f6adc18b61f2f
7
- data.tar.gz: 119cece41b0a7c469be3df3c6db040d67de82f82c389fd3b5695300d361cdd37b0b70e2d1ad1e8604a61802b9e8dd3159fef92b158bdce35238c40a98157df47
6
+ metadata.gz: b2853c5308ce93417b2efd259bf34a73d31055d52569bc64549306895b985cb80e2585de6941a51170f071b1057ec4265b54da13d25666ec260f72ace494881f
7
+ data.tar.gz: 248707f263ef74d7c6847d656de2413dd20f8d235cbb94aa9cad25a9d94f51df0eeb055e3ba28af1499822fa2302f7137097fd4addf8d07b14e341aea38b330f
data/CHANGELOG CHANGED
@@ -2,6 +2,51 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.96.0 (Jul 26, 2019)
6
+ * Bluesnap: Omit state codes for unsupported countries [therufs] #3229
7
+ * Adyen: Pass updateShopperStatement, industryUsage [curiousepic] #3233
8
+ * TransFirst Transaction Express: Fix blank address2 values [britth] #3231
9
+ * WorldPay: Add support for store method [bayprogrammer] #3232
10
+ * Adyen: Support for additional AVS code mapping [jknipp] #3236
11
+ * Adyen: Update message for AVS result code 'A' to generically cover postal code mismatches [jknipp] #3237
12
+ * CyberSource: Update CyberSource SOAP documentation link [vince-smith] #3204
13
+ * USAePay: Handle additional error codes and add default error code [estelendur] #3167
14
+ * Braintree: Add `skip_avs` and `skip_cvv` gateway specific fields [leila-alderman] #3241
15
+ * NAB Transact: Update periodic test url [mengqing] #3177
16
+ * NMI: Add level 3 gateway-specific fields tax, shipping, and ponumber [jasonxp] #3239
17
+ * Checkout V2: Update stored card flag [curiousepic] #3247
18
+ * NMI: Add support for stored credentials [bayprogrammer] #3243
19
+ * Spreedly: Consolidate API requests and support bank accounts [lancecarlson] #3105
20
+ * BPoint: Hook up merchant_reference and CRN fields [curiousepic] #3249
21
+ * Checkout V2: Stop sending phone number to Checkout V2 integration [filipebarcos] #3248
22
+ * Barclaycard Smartpay: Add support for 3DS2 [britth] #3251
23
+ * Adyen: Add support for non-fractional currencies [molbrown] #3257
24
+ * Decidir: Add new gateway [jknipp] #3254
25
+ * Checkout V2: Reapply Update stored card flag [curiousepic]
26
+ * CyberSource: Update supported countries [molbrown] #3260
27
+ * Credorax: Update supported countries [molbrown] #3260
28
+ * Kushki: Update supported countries [molbrown] #3260
29
+ * Paypal: Update supported countries [molbrown] #3260
30
+ * BlueSnap: Send amount in capture requests [jknipp] #3262
31
+ * Mundipagg: Add Alelo card support [jasonxp] #3255
32
+ * Adyen: Remove temporary amount modification for non-fractional currencies [molbrown] #3263
33
+ * Adyen: Set blank state to N/A [therufs] #3252
34
+ * MiGS: Add tx_source gateway specific field [leila-alderman] #3264
35
+ * NMI: Correct password scrubber to scrub symbols [hdeters] #3267
36
+ * Global Collect: Only add name if present [curiousepic] #3268
37
+ * HPS: Add Apple Pay raw cryptogram support [slogsdon] #3209
38
+ * CardConnect: Fix parsing of level 3 fields [hdeters] #3273
39
+ * TrustCommerce: Support void after purchase [jknipp] #3265
40
+ * Payflow: Support arbitrary level 2 + level 3 fields [therufs] #3272
41
+ * BlueSnap: Default to not send amount on capture [molbrown] #3270
42
+ * Spreedly: extra fields, remove extraneous check [montdidier] #3102 #3281
43
+ * Cecabank: Update encryption to SHA2 [leila-alderman] #3278
44
+ * Checkout V2: Fix 3DS 1&2 integration [nicolas-maalouf-cko] #3240
45
+ * Credorax: add 3DS2 MPI auth data support [bayprogrammer] #3274
46
+ * Add Kosovo to the list of countries [AnotherJoSmith] #3226
47
+ * Realex: Adds 3DS 1&2 support through external MPI [filipebarcos] #3284
48
+ * PayPal: Adds 3DS 1 support through external MPI [nebdil] #3279
49
+
5
50
  == Version 1.95.0 (May 23, 2019)
6
51
  * Adyen: Constantize version to fix subdomains [curiousepic] #3228
7
52
  * Qvalent: Adds support for standard stored credential framework [molbrown] #3227
data/README.md CHANGED
@@ -84,6 +84,9 @@ end
84
84
  For more in-depth documentation and tutorials, see [GettingStarted.md](GettingStarted.md) and the
85
85
  [API documentation](http://www.rubydoc.info/github/activemerchant/active_merchant/).
86
86
 
87
+ Emerging ActiveMerchant 3DS conventions are documented in the [Contributing](https://github.com/activemerchant/active_merchant/wiki/Contributing#3ds-options)
88
+ guide and [Standardized 3DS Fields](https://github.com/activemerchant/active_merchant/wiki/Standardized-3DS-Fields) guide of the wiki.
89
+
87
90
  ## Supported Payment Gateways
88
91
 
89
92
  The [ActiveMerchant Wiki](https://github.com/activemerchant/active_merchant/wikis) contains a [table of features supported by each gateway](https://github.com/activemerchant/active_merchant/wiki/Gateway-Feature-Matrix).
@@ -3,14 +3,13 @@
3
3
  module ActiveMerchant
4
4
  module Billing
5
5
  # Implements the Address Verification System
6
- # https://www.wellsfargo.com/downloads/pdf/biz/merchant/visa_avs.pdf
6
+ # https://www.cybersource.com/developers/other_resources/quick_references/avs_results/.
7
7
  # http://en.wikipedia.org/wiki/Address_Verification_System
8
- # http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_IG/html/app_avs_cvn_codes.htm#app_AVS_CVN_codes_7891_48375
9
- # http://imgserver.skipjack.com/imgServer/5293710/AVS%20and%20CVV2.pdf
10
8
  # http://www.emsecommerce.net/avs_cvv2_response_codes.htm
9
+ # https://www.cardfellow.com/blog/address-verification-service-avs/
11
10
  class AVSResult
12
11
  MESSAGES = {
13
- 'A' => 'Street address matches, but 5-digit and 9-digit postal code do not match.',
12
+ 'A' => 'Street address matches, but postal code does not match.',
14
13
  'B' => 'Street address matches, but postal code not verified.',
15
14
  'C' => 'Street address and postal code do not match.',
16
15
  'D' => 'Street address and postal code match.',
@@ -23,7 +22,7 @@ module ActiveMerchant
23
22
  'K' => 'Card member\'s name matches but billing address and billing postal code do not match.',
24
23
  'L' => 'Card member\'s name and billing postal code match, but billing address does not match.',
25
24
  'M' => 'Street address and postal code match.',
26
- 'N' => 'Street address and postal code do not match.',
25
+ 'N' => 'Street address and postal code do not match. For American Express: Card member\'s name, street address and postal code do not match.',
27
26
  'O' => 'Card member\'s name and billing address match, but billing postal code does not match.',
28
27
  'P' => 'Postal code matches, but street address not verified.',
29
28
  'Q' => 'Card member\'s name, billing address, and postal code match. Shipping information verified but chargeback protection not guaranteed.',
@@ -19,6 +19,7 @@ module ActiveMerchant #:nodoc:
19
19
  # * Maestro
20
20
  # * Forbrugsforeningen
21
21
  # * Elo
22
+ # * Alelo
22
23
  #
23
24
  # For testing purposes, use the 'bogus' credit card brand. This skips the vast majority of
24
25
  # validations, allowing you to focus on your core concerns until you're ready to be more concerned
@@ -90,6 +91,7 @@ module ActiveMerchant #:nodoc:
90
91
  # * +'maestro'+
91
92
  # * +'forbrugsforeningen'+
92
93
  # * +'elo'+
94
+ # * +'alelo'+
93
95
  #
94
96
  # Or, if you wish to test your implementation, +'bogus'+.
95
97
  #
@@ -6,6 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
  'visa' => ->(num) { num =~ /^4\d{12}(\d{3})?(\d{3})?$/ },
7
7
  'master' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), MASTERCARD_RANGES) },
8
8
  'elo' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), ELO_RANGES) },
9
+ 'alelo' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), ALELO_RANGES) },
9
10
  'discover' => ->(num) { num =~ /^(6011|65\d{2}|64[4-9]\d)\d{12,15}|(62\d{14,17})$/ },
10
11
  'american_express' => ->(num) { num =~ /^3[47]\d{13}$/ },
11
12
  'diners_club' => ->(num) { num =~ /^3(0[0-5]|[68]\d)\d{11}$/ },
@@ -79,6 +80,19 @@ module ActiveMerchant #:nodoc:
79
80
  651652..651667, 651675..651678, 655000..655010, 655012..655015, 655051..655052, 655056..655057
80
81
  ]
81
82
 
83
+ # Alelo provides BIN ranges by e-mailing them out periodically.
84
+ # The BINs beginning with the digit 4 overlap with Visa's range of valid card numbers.
85
+ # By placing the 'alelo' entry in CARD_COMPANY_DETECTORS below the 'visa' entry, we
86
+ # identify these cards as Visa. This works because transactions with such cards will
87
+ # run on Visa rails.
88
+ ALELO_RANGES = [
89
+ 402588..402588, 404347..404347, 405876..405876, 405882..405882, 405884..405884,
90
+ 405886..405886, 430471..430471, 438061..438061, 438064..438064, 470063..470066,
91
+ 496067..496067, 506699..506704, 506706..506706, 506713..506714, 506716..506716,
92
+ 506749..506750, 506752..506752, 506754..506756, 506758..506762, 506764..506767,
93
+ 506770..506771, 509015..509019, 509880..509882, 509884..509885, 509987..509988
94
+ ]
95
+
82
96
  def self.included(base)
83
97
  base.extend(ClassMethods)
84
98
  end
@@ -200,6 +200,16 @@ module ActiveMerchant #:nodoc:
200
200
  false
201
201
  end
202
202
 
203
+ def add_fields_to_post_if_present(post, options, fields)
204
+ fields.each do |field|
205
+ add_field_to_post_if_present(post, options, field)
206
+ end
207
+ end
208
+
209
+ def add_field_to_post_if_present(post, options, field)
210
+ post[field] = options[field] if options[field]
211
+ end
212
+
203
213
  protected # :nodoc: all
204
214
 
205
215
  def normalize(field)
@@ -9,6 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
 
10
10
  self.supported_countries = ['AT', 'AU', 'BE', 'BG', 'BR', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GI', 'GR', 'HK', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'MX', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SG', 'SK', 'SI', 'US']
11
11
  self.default_currency = 'USD'
12
+ self.currencies_without_fractions = %w(CVE DJF GNF IDR JPY KMF KRW PYG RWF UGX VND VUV XAF XOF XPF)
12
13
  self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover, :elo]
13
14
 
14
15
  self.money_format = :cents
@@ -140,10 +141,14 @@ module ActiveMerchant #:nodoc:
140
141
  '16' => 'N', # Postal code doesn't match, address unknown
141
142
  '17' => 'U', # Postal code doesn't match, address not checked
142
143
  '18' => 'I', # Neither postal code nor address were checked
144
+ '19' => 'L', # Name and postal code matches.
143
145
  '20' => 'V', # Name, address and postal code matches.
146
+ '21' => 'O', # Name and address matches.
147
+ '22' => 'K', # Name matches.
144
148
  '23' => 'F', # Postal code matches, name doesn't match.
145
149
  '24' => 'H', # Both postal code and address matches, name doesn't match.
146
- '25' => 'T' # Address matches, name doesn't match.
150
+ '25' => 'T', # Address matches, name doesn't match.
151
+ '26' => 'N' # Neither postal code, address nor name matches.
147
152
  }
148
153
 
149
154
  CVC_MAPPING = {
@@ -179,6 +184,8 @@ module ActiveMerchant #:nodoc:
179
184
  post[:additionalData]['paymentdatasource.type'] = NETWORK_TOKENIZATION_CARD_SOURCE[payment.source.to_s] if payment.is_a?(NetworkTokenizationCreditCard)
180
185
  post[:additionalData][:authorisationType] = options[:authorisation_type] if options[:authorisation_type]
181
186
  post[:additionalData][:adjustAuthorisationData] = options[:adjust_authorisation_data] if options[:adjust_authorisation_data]
187
+ post[:additionalData][:industryUsage] = options[:industry_usage] if options[:industry_usage]
188
+ post[:additionalData][:updateShopperStatement] = options[:update_shopper_statement] if options[:update_shopper_statement]
182
189
  post[:additionalData][:RequestedTestAcquirerResponseCode] = options[:requested_test_acquirer_response_code] if options[:requested_test_acquirer_response_code] && test?
183
190
  post[:deviceFingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
184
191
  add_risk_data(post, options)
@@ -225,23 +232,29 @@ module ActiveMerchant #:nodoc:
225
232
  post[:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
226
233
  post[:billingAddress][:postalCode] = address[:zip] if address[:zip]
227
234
  post[:billingAddress][:city] = address[:city] || 'N/A'
228
- post[:billingAddress][:stateOrProvince] = address[:state] || 'N/A'
235
+ post[:billingAddress][:stateOrProvince] = get_state(address)
229
236
  post[:billingAddress][:country] = address[:country] if address[:country]
230
237
  end
231
238
  end
232
239
 
240
+ def get_state(address)
241
+ address[:state] && !address[:state].blank? ? address[:state] : 'N/A'
242
+ end
243
+
233
244
  def add_invoice(post, money, options)
245
+ currency = options[:currency] || currency(money)
234
246
  amount = {
235
- value: amount(money),
236
- currency: options[:currency] || currency(money)
247
+ value: localized_amount(money, currency),
248
+ currency: currency
237
249
  }
238
250
  post[:amount] = amount
239
251
  end
240
252
 
241
253
  def add_invoice_for_modification(post, money, options)
254
+ currency = options[:currency] || currency(money)
242
255
  amount = {
243
- value: amount(money),
244
- currency: options[:currency] || currency(money)
256
+ value: localized_amount(money, currency),
257
+ currency: currency
245
258
  }
246
259
  post[:modificationAmount] = amount
247
260
  end
@@ -13,7 +13,7 @@ module ActiveMerchant #:nodoc:
13
13
  self.homepage_url = 'https://www.barclaycardsmartpay.com/'
14
14
  self.display_name = 'Barclaycard Smartpay'
15
15
 
16
- API_VERSION = 'v30'
16
+ API_VERSION = 'v40'
17
17
 
18
18
  def initialize(options = {})
19
19
  requires!(options, :company, :merchant, :password)
@@ -37,7 +37,7 @@ module ActiveMerchant #:nodoc:
37
37
  post[:card] = credit_card_hash(creditcard)
38
38
  post[:billingAddress] = billing_address_hash(options) if options[:billing_address]
39
39
  post[:deliveryAddress] = shipping_address_hash(options) if options[:shipping_address]
40
- add_3ds(post, options) if options[:execute_threed]
40
+ add_3ds(post, options)
41
41
  commit('authorise', post)
42
42
  end
43
43
 
@@ -186,7 +186,7 @@ module ActiveMerchant #:nodoc:
186
186
  end
187
187
 
188
188
  def parse_avs_code(response)
189
- AVS_MAPPING[response['avsResult'][0..1].strip] if response['avsResult']
189
+ AVS_MAPPING[response['additionalData']['avsResult'][0..1].strip] if response.dig('additionalData', 'avsResult')
190
190
  end
191
191
 
192
192
  def flatten_hash(hash, prefix = nil)
@@ -210,12 +210,18 @@ module ActiveMerchant #:nodoc:
210
210
  end
211
211
 
212
212
  def parse(response)
213
- Hash[
214
- response.split('&').map do |x|
215
- key, val = x.split('=', 2)
216
- [key.split('.').last, CGI.unescape(val)]
213
+ parsed_response = {}
214
+ params = CGI.parse(response)
215
+ params.each do |key, value|
216
+ parsed_key = key.split('.', 2)
217
+ if parsed_key.size > 1
218
+ parsed_response[parsed_key[0]] ||= {}
219
+ parsed_response[parsed_key[0]][parsed_key[1]] = value[0]
220
+ else
221
+ parsed_response[parsed_key[0]] = value[0]
217
222
  end
218
- ]
223
+ end
224
+ parsed_response
219
225
  end
220
226
 
221
227
  def post_data(data)
@@ -343,8 +349,31 @@ module ActiveMerchant #:nodoc:
343
349
  end
344
350
 
345
351
  def add_3ds(post, options)
346
- post[:additionalData] = { executeThreeD: 'true' }
347
- post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
352
+ if three_ds_2_options = options[:three_ds_2]
353
+ if browser_info = three_ds_2_options[:browser_info]
354
+ post[:browserInfo] = {
355
+ acceptHeader: browser_info[:accept_header],
356
+ colorDepth: browser_info[:depth],
357
+ javaEnabled: browser_info[:java],
358
+ language: browser_info[:language],
359
+ screenHeight: browser_info[:height],
360
+ screenWidth: browser_info[:width],
361
+ timeZoneOffset: browser_info[:timezone],
362
+ userAgent: browser_info[:user_agent]
363
+ }
364
+
365
+ if device_channel = three_ds_2_options[:channel]
366
+ post[:threeDS2RequestData] = {
367
+ deviceChannel: device_channel,
368
+ notificationURL: three_ds_2_options[:notification_url]
369
+ }
370
+ end
371
+ end
372
+ else
373
+ return unless options[:execute_threed] || options[:threed_dynamic]
374
+ post[:browserInfo] = { userAgent: options[:user_agent], acceptHeader: options[:accept_header] }
375
+ post[:additionalData] = { executeThreeD: 'true' } if options[:execute_threed]
376
+ end
348
377
  end
349
378
  end
350
379
  end
@@ -66,6 +66,8 @@ module ActiveMerchant
66
66
  'business_savings' => 'CORPORATE_SAVINGS'
67
67
  }
68
68
 
69
+ STATE_CODE_COUNTRIES = %w(US CA)
70
+
69
71
  def initialize(options={})
70
72
  requires!(options, :api_username, :api_password)
71
73
  super
@@ -93,6 +95,7 @@ module ActiveMerchant
93
95
  commit(:capture, :put) do |doc|
94
96
  add_authorization(doc, authorization)
95
97
  add_order(doc, options)
98
+ add_amount(doc, money, options) if options[:include_capture_amount] == true
96
99
  end
97
100
  end
98
101
 
@@ -228,7 +231,7 @@ module ActiveMerchant
228
231
  return unless address
229
232
 
230
233
  doc.country(address[:country]) if address[:country]
231
- doc.state(address[:state]) if address[:state]
234
+ doc.state(address[:state]) if address[:state] && STATE_CODE_COUNTRIES.include?(address[:country])
232
235
  doc.address(address[:address]) if address[:address]
233
236
  doc.city(address[:city]) if address[:city]
234
237
  doc.zip(address[:zip]) if address[:zip]
@@ -163,10 +163,10 @@ module ActiveMerchant #:nodoc:
163
163
  xml.send('PaymentType', payment_type)
164
164
  xml.send('TxnType', 'WEB_SHOP')
165
165
  xml.send('BillerCode', options.fetch(:biller_code, ''))
166
- xml.send('MerchantReference', '')
167
- xml.send('CRN1', '')
168
- xml.send('CRN2', '')
169
- xml.send('CRN3', '')
166
+ xml.send('MerchantReference', options[:order_id]) if options[:order_id]
167
+ xml.send('CRN1', options[:crn1]) if options[:crn1]
168
+ xml.send('CRN2', options[:crn2]) if options[:crn2]
169
+ xml.send('CRN3', options[:crn3]) if options[:crn3]
170
170
  xml.send('Amount', amount)
171
171
  end
172
172
 
@@ -419,7 +419,9 @@ module ActiveMerchant #:nodoc:
419
419
  'street: A, zip: N' => 'C',
420
420
  'street: A, zip: U' => 'I',
421
421
  'street: A, zip: I' => 'I',
422
- 'street: A, zip: A' => 'I'
422
+ 'street: A, zip: A' => 'I',
423
+
424
+ 'street: B, zip: B' => 'B'
423
425
  }
424
426
  end
425
427
 
@@ -590,6 +592,14 @@ module ActiveMerchant #:nodoc:
590
592
  parameters[:options][:skip_advanced_fraud_checking] = options[:skip_advanced_fraud_checking]
591
593
  end
592
594
 
595
+ if options[:skip_avs]
596
+ parameters[:options][:skip_avs] = options[:skip_avs]
597
+ end
598
+
599
+ if options[:skip_cvv]
600
+ parameters[:options][:skip_cvv] = options[:skip_cvv]
601
+ end
602
+
593
603
  parameters[:custom_fields] = options[:custom_fields]
594
604
  parameters[:device_data] = options[:device_data] if options[:device_data]
595
605
  parameters[:service_fee_amount] = options[:service_fee_amount] if options[:service_fee_amount]
@@ -233,6 +233,7 @@ module ActiveMerchant #:nodoc:
233
233
  item.each_pair do |k, v|
234
234
  updated.merge!(k.to_s.gsub(/_/, '') => v)
235
235
  end
236
+ updated
236
237
  end
237
238
  end
238
239
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class CecabankGateway < Gateway
4
- self.test_url = 'http://tpv.ceca.es:8000'
4
+ self.test_url = 'https://tpv.ceca.es'
5
5
  self.live_url = 'https://pgw.ceca.es'
6
6
 
7
7
  self.supported_countries = ['ES']
@@ -13,14 +13,14 @@ module ActiveMerchant #:nodoc:
13
13
 
14
14
  #### CECA's MAGIC NUMBERS
15
15
  CECA_NOTIFICATIONS_URL = 'NONE'
16
- CECA_ENCRIPTION = 'SHA1'
16
+ CECA_ENCRIPTION = 'SHA2'
17
17
  CECA_DECIMALS = '2'
18
18
  CECA_MODE = 'SSL'
19
19
  CECA_UI_LESS_LANGUAGE = 'XML'
20
20
  CECA_UI_LESS_LANGUAGE_REFUND = '1'
21
21
  CECA_UI_LESS_REFUND_PAGE = 'anulacion_xml'
22
- CECA_ACTION_REFUND = 'tpvanularparcialmente' # use partial refund's URL to avoid time frame limitations and decision logic on client side
23
- CECA_ACTION_PURCHASE = 'tpv'
22
+ CECA_ACTION_REFUND = 'anulaciones/anularParcial' # use partial refund's URL to avoid time frame limitations and decision logic on client side
23
+ CECA_ACTION_PURCHASE = 'tpv/compra'
24
24
  CECA_CURRENCIES_DICTIONARY = {'EUR' => 978, 'USD' => 840, 'GBP' => 826}
25
25
 
26
26
  # Creates a new CecabankGateway
@@ -168,8 +168,8 @@ module ActiveMerchant #:nodoc:
168
168
  'AcquirerBIN' => options[:acquirer_bin],
169
169
  'TerminalID' => options[:terminal_id]
170
170
  )
171
- url = (test? ? self.test_url : self.live_url) + "/cgi-bin/#{action}"
172
- xml = ssl_post(url, post_data(parameters))
171
+ url = (test? ? self.test_url : self.live_url) + "/tpvweb/#{action}.action"
172
+ xml = ssl_post("#{url}?", post_data(parameters))
173
173
  response = parse(xml)
174
174
  Response.new(
175
175
  response[:success],
@@ -242,7 +242,7 @@ module ActiveMerchant #:nodoc:
242
242
  CECA_NOTIFICATIONS_URL +
243
243
  CECA_NOTIFICATIONS_URL
244
244
  end
245
- Digest::SHA1.hexdigest(signature_fields)
245
+ Digest::SHA2.hexdigest(signature_fields)
246
246
  end
247
247
  end
248
248
  end
@@ -9,14 +9,14 @@ module ActiveMerchant #:nodoc:
9
9
  self.supported_countries = ['AD', 'AE', 'AT', 'BE', 'BG', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GI', 'GL', 'GR', 'HR', 'HU', 'IE', 'IS', 'IL', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SI', 'SM', 'SK', 'SJ', 'TR', 'VA']
10
10
  self.default_currency = 'USD'
11
11
  self.money_format = :cents
12
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :maestro, :discover]
12
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :maestro, :discover]
13
13
 
14
- def initialize(options={})
14
+ def initialize(options = {})
15
15
  requires!(options, :secret_key)
16
16
  super
17
17
  end
18
18
 
19
- def purchase(amount, payment_method, options={})
19
+ def purchase(amount, payment_method, options = {})
20
20
  multi = MultiResponse.run do |r|
21
21
  r.process { authorize(amount, payment_method, options) }
22
22
  r.process { capture(amount, r.authorization, options) }
@@ -28,11 +28,11 @@ module ActiveMerchant #:nodoc:
28
28
  response(:purchase, succeeded, merged_params)
29
29
  end
30
30
 
31
- def authorize(amount, payment_method, options={})
31
+ def authorize(amount, payment_method, options = {})
32
32
  post = {}
33
33
  post[:capture] = false
34
34
  add_invoice(post, amount, options)
35
- add_payment_method(post, payment_method)
35
+ add_payment_method(post, payment_method, options)
36
36
  add_customer_data(post, options)
37
37
  add_transaction_data(post, options)
38
38
  add_3ds(post, options)
@@ -40,7 +40,7 @@ module ActiveMerchant #:nodoc:
40
40
  commit(:authorize, post)
41
41
  end
42
42
 
43
- def capture(amount, authorization, options={})
43
+ def capture(amount, authorization, options = {})
44
44
  post = {}
45
45
  add_invoice(post, amount, options)
46
46
  add_customer_data(post, options)
@@ -48,12 +48,12 @@ module ActiveMerchant #:nodoc:
48
48
  commit(:capture, post, authorization)
49
49
  end
50
50
 
51
- def void(authorization, options={})
51
+ def void(authorization, _options = {})
52
52
  post = {}
53
53
  commit(:void, post, authorization)
54
54
  end
55
55
 
56
- def refund(amount, authorization, options={})
56
+ def refund(amount, authorization, options = {})
57
57
  post = {}
58
58
  add_invoice(post, amount, options)
59
59
  add_customer_data(post, options)
@@ -61,7 +61,7 @@ module ActiveMerchant #:nodoc:
61
61
  commit(:refund, post, authorization)
62
62
  end
63
63
 
64
- def verify(credit_card, options={})
64
+ def verify(credit_card, options = {})
65
65
  MultiResponse.run(:use_first_response) do |r|
66
66
  r.process { authorize(100, credit_card, options) }
67
67
  r.process(:ignore_result) { void(r.authorization, options) }
@@ -74,9 +74,9 @@ module ActiveMerchant #:nodoc:
74
74
 
75
75
  def scrub(transcript)
76
76
  transcript.
77
- gsub(%r((Authorization: )[^\\]*)i, '\1[FILTERED]').
78
- gsub(%r(("number\\":\\")\d+), '\1[FILTERED]').
79
- gsub(%r(("cvv\\":\\")\d+), '\1[FILTERED]')
77
+ gsub(/(Authorization: )[^\\]*/i, '\1[FILTERED]').
78
+ gsub(/("number\\":\\")\d+/, '\1[FILTERED]').
79
+ gsub(/("cvv\\":\\")\d+/, '\1[FILTERED]')
80
80
  end
81
81
 
82
82
  private
@@ -94,7 +94,7 @@ module ActiveMerchant #:nodoc:
94
94
  post[:metadata][:udf5] = application_id || 'ActiveMerchant'
95
95
  end
96
96
 
97
- def add_payment_method(post, payment_method)
97
+ def add_payment_method(post, payment_method, options)
98
98
  post[:source] = {}
99
99
  post[:source][:type] = 'card'
100
100
  post[:source][:name] = payment_method.name
@@ -102,6 +102,7 @@ module ActiveMerchant #:nodoc:
102
102
  post[:source][:cvv] = payment_method.verification_value
103
103
  post[:source][:expiry_year] = format(payment_method.year, :four_digits)
104
104
  post[:source][:expiry_month] = format(payment_method.month, :two_digits)
105
+ post[:source][:stored] = 'true' if options[:card_on_file] == true
105
106
  end
106
107
 
107
108
  def add_customer_data(post, options)
@@ -109,7 +110,7 @@ module ActiveMerchant #:nodoc:
109
110
  post[:customer][:email] = options[:email] || nil
110
111
  post[:payment_ip] = options[:ip] if options[:ip]
111
112
  address = options[:billing_address]
112
- if(address && post[:source])
113
+ if address && post[:source]
113
114
  post[:source][:billing_address] = {}
114
115
  post[:source][:billing_address][:address_line1] = address[:address1] unless address[:address1].blank?
115
116
  post[:source][:billing_address][:address_line2] = address[:address2] unless address[:address2].blank?
@@ -117,12 +118,10 @@ module ActiveMerchant #:nodoc:
117
118
  post[:source][:billing_address][:state] = address[:state] unless address[:state].blank?
118
119
  post[:source][:billing_address][:country] = address[:country] unless address[:country].blank?
119
120
  post[:source][:billing_address][:zip] = address[:zip] unless address[:zip].blank?
120
- post[:source][:phone] = { number: address[:phone] } unless address[:phone].blank?
121
121
  end
122
122
  end
123
123
 
124
- def add_transaction_data(post, options={})
125
- post[:card_on_file] = true if options[:card_on_file] == true
124
+ def add_transaction_data(post, options = {})
126
125
  post[:payment_type] = 'Regular' if options[:transaction_indicator] == 1
127
126
  post[:payment_type] = 'Recurring' if options[:transaction_indicator] == 2
128
127
  post[:previous_payment_id] = options[:previous_charge_id] if options[:previous_charge_id]
@@ -132,9 +131,10 @@ module ActiveMerchant #:nodoc:
132
131
  if options[:three_d_secure]
133
132
  post[:'3ds'] = {}
134
133
  post[:'3ds'][:enabled] = true
135
- post[:'3ds'][:eci] = options[:eci] if options[:eci]
136
- post[:'3ds'][:cryptogram] = options[:cavv] if options[:cavv]
137
- post[:'3ds'][:xid] = options[:xid] if options[:xid]
134
+ post[:'3ds'][:eci] = options[:three_d_secure][:eci] if options[:three_d_secure][:eci]
135
+ post[:'3ds'][:cryptogram] = options[:three_d_secure][:cavv] if options[:three_d_secure][:cavv]
136
+ post[:'3ds'][:version] = options[:three_d_secure][:version] if options[:three_d_secure][:version]
137
+ post[:'3ds'][:xid] = options[:three_d_secure][:ds_transaction_id] || options[:three_d_secure][:xid]
138
138
  end
139
139
  end
140
140
 
@@ -146,7 +146,7 @@ module ActiveMerchant #:nodoc:
146
146
  response['id'] = response['_links']['payment']['href'].split('/')[-1]
147
147
  end
148
148
  rescue ResponseError => e
149
- raise unless(e.response.code.to_s =~ /4\d\d/)
149
+ raise unless e.response.code.to_s =~ /4\d\d/
150
150
  response = parse(e.response.body)
151
151
  end
152
152
 
@@ -175,11 +175,11 @@ module ActiveMerchant #:nodoc:
175
175
  def headers
176
176
  {
177
177
  'Authorization' => @options[:secret_key],
178
- 'Content-Type' => 'application/json;charset=UTF-8'
178
+ 'Content-Type' => 'application/json;charset=UTF-8',
179
179
  }
180
180
  end
181
181
 
182
- def url(post, action, authorization)
182
+ def url(_post, action, authorization)
183
183
  if action == :authorize
184
184
  "#{base_url}/payments"
185
185
  elsif action == :capture
@@ -248,7 +248,7 @@ module ActiveMerchant #:nodoc:
248
248
  def error_code_from(succeeded, response)
249
249
  return if succeeded
250
250
  if response['error_type'] && response['error_codes']
251
- "#{response["error_type"]}: #{response["error_codes"].join(", ")}"
251
+ "#{response['error_type']}: #{response['error_codes'].join(', ')}"
252
252
  elsif response['error_type']
253
253
  response['error_type']
254
254
  else