activemerchant 1.94.0 → 1.99.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +120 -1
  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 +6 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +65 -2
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +119 -34
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +11 -6
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -2
  13. data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +56 -9
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +2 -1
  16. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -7
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +37 -27
  18. data/lib/active_merchant/billing/gateways/credorax.rb +69 -4
  19. data/lib/active_merchant/billing/gateways/cyber_source.rb +51 -11
  20. data/lib/active_merchant/billing/gateways/d_local.rb +1 -1
  21. data/lib/active_merchant/billing/gateways/decidir.rb +245 -0
  22. data/lib/active_merchant/billing/gateways/epay.rb +13 -2
  23. data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
  24. data/lib/active_merchant/billing/gateways/fat_zebra.rb +6 -0
  25. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
  26. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  27. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/mastercard.rb +30 -5
  29. data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/migs.rb +8 -0
  31. data/lib/active_merchant/billing/gateways/monei.rb +31 -0
  32. data/lib/active_merchant/billing/gateways/mundipagg.rb +33 -6
  33. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  34. data/lib/active_merchant/billing/gateways/nmi.rb +39 -1
  35. data/lib/active_merchant/billing/gateways/opp.rb +20 -1
  36. data/lib/active_merchant/billing/gateways/orbital.rb +60 -10
  37. data/lib/active_merchant/billing/gateways/payflow.rb +64 -14
  38. data/lib/active_merchant/billing/gateways/paymill.rb +5 -0
  39. data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
  40. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -2
  41. data/lib/active_merchant/billing/gateways/qvalent.rb +43 -1
  42. data/lib/active_merchant/billing/gateways/realex.rb +32 -9
  43. data/lib/active_merchant/billing/gateways/redsys.rb +113 -30
  44. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  45. data/lib/active_merchant/billing/gateways/stripe.rb +38 -9
  46. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +271 -0
  47. data/lib/active_merchant/billing/gateways/tns.rb +10 -5
  48. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -2
  49. data/lib/active_merchant/billing/gateways/trust_commerce.rb +45 -6
  50. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -5
  51. data/lib/active_merchant/billing/gateways/worldpay.rb +177 -39
  52. data/lib/active_merchant/country.rb +1 -0
  53. data/lib/active_merchant/version.rb +1 -1
  54. metadata +19 -4
@@ -6,6 +6,10 @@ module ActiveMerchant #:nodoc:
6
6
  self.display_name = 'Credorax Gateway'
7
7
  self.homepage_url = 'https://www.credorax.com/'
8
8
 
9
+ # NOTE: the IP address you run the remote tests from will need to be
10
+ # whitelisted by Credorax; contact support@credorax.com as necessary to
11
+ # request your IP address be added to the whitelist for your test
12
+ # account.
9
13
  self.test_url = 'https://intconsole.credorax.com/intenv/service/gateway'
10
14
 
11
15
  # The live URL is assigned on a per merchant basis once certification has passed
@@ -15,7 +19,7 @@ module ActiveMerchant #:nodoc:
15
19
  # ActiveMerchant::Billing::CredoraxGateway.live_url = "https://assigned-subdomain.credorax.net/crax_gate/service/gateway"
16
20
  self.live_url = 'https://assigned-subdomain.credorax.net/crax_gate/service/gateway'
17
21
 
18
- self.supported_countries = %w(DE GB FR IT ES PL NL BE GR CZ PT SE HU RS AT CH BG DK FI SK NO IE HR BA AL LT MK SI LV EE ME LU MT IS AD MC LI SM)
22
+ self.supported_countries = %w(AD AT BE BG HR CY CZ DK EE FR DE GI GR GG HU IS IE IM IT JE LV LI LT LU MT MC NO PL PT RO SM SK ES SE CH GB)
19
23
  self.default_currency = 'EUR'
20
24
  self.currencies_without_fractions = %w(CLP JPY KRW PYG VND)
21
25
  self.currencies_with_three_decimal_places = %w(BHD JOD KWD OMR RSD TND)
@@ -131,6 +135,7 @@ module ActiveMerchant #:nodoc:
131
135
  add_echo(post, options)
132
136
  add_submerchant_id(post, options)
133
137
  add_transaction_type(post, options)
138
+ add_processor(post, options)
134
139
 
135
140
  commit(:purchase, post)
136
141
  end
@@ -145,6 +150,7 @@ module ActiveMerchant #:nodoc:
145
150
  add_echo(post, options)
146
151
  add_submerchant_id(post, options)
147
152
  add_transaction_type(post, options)
153
+ add_processor(post, options)
148
154
 
149
155
  commit(:authorize, post)
150
156
  end
@@ -156,6 +162,7 @@ module ActiveMerchant #:nodoc:
156
162
  add_customer_data(post, options)
157
163
  add_echo(post, options)
158
164
  add_submerchant_id(post, options)
165
+ add_processor(post, options)
159
166
 
160
167
  commit(:capture, post)
161
168
  end
@@ -167,6 +174,7 @@ module ActiveMerchant #:nodoc:
167
174
  add_echo(post, options)
168
175
  add_submerchant_id(post, options)
169
176
  post[:a1] = generate_unique_id
177
+ add_processor(post, options)
170
178
 
171
179
  commit(:void, post, reference_action)
172
180
  end
@@ -178,6 +186,7 @@ module ActiveMerchant #:nodoc:
178
186
  add_customer_data(post, options)
179
187
  add_echo(post, options)
180
188
  add_submerchant_id(post, options)
189
+ add_processor(post, options)
181
190
 
182
191
  commit(:refund, post)
183
192
  end
@@ -191,6 +200,7 @@ module ActiveMerchant #:nodoc:
191
200
  add_echo(post, options)
192
201
  add_submerchant_id(post, options)
193
202
  add_transaction_type(post, options)
203
+ add_processor(post, options)
194
204
 
195
205
  commit(:credit, post)
196
206
  end
@@ -264,8 +274,56 @@ module ActiveMerchant #:nodoc:
264
274
  end
265
275
 
266
276
  def add_3d_secure(post, options)
267
- return unless options[:eci] && options[:xid]
268
- post[:i8] = "#{options[:eci]}:#{(options[:cavv] || "none")}:#{options[:xid]}"
277
+ if options[:eci] && options[:xid]
278
+ add_3d_secure_1_data(post, options)
279
+ elsif options[:execute_threed] && options[:three_ds_2]
280
+ three_ds_2_options = options[:three_ds_2]
281
+ browser_info = three_ds_2_options[:browser_info]
282
+ post[:'3ds_initiate'] = options[:three_ds_initiate] || '01'
283
+ post[:'3ds_purchasedate'] = Time.now.utc.strftime('%Y%m%d%I%M%S')
284
+ post[:'3ds_channel'] = '02'
285
+ post[:'3ds_redirect_url'] = three_ds_2_options[:notification_url]
286
+ post[:'3ds_challengewindowsize'] = options[:three_ds_challenge_window_size] || '03'
287
+ post[:'3ds_version'] = options[:three_ds_version] if options[:three_ds_version]
288
+ post[:d5] = browser_info[:user_agent]
289
+ post[:'3ds_transtype'] = options[:transaction_type] || '01'
290
+ post[:'3ds_browsertz'] = browser_info[:timezone]
291
+ post[:'3ds_browserscreenwidth'] = browser_info[:width]
292
+ post[:'3ds_browserscreenheight'] = browser_info[:height]
293
+ post[:'3ds_browsercolordepth'] = browser_info[:depth]
294
+ post[:d6] = browser_info[:language]
295
+ post[:'3ds_browserjavaenabled'] = browser_info[:java]
296
+ post[:'3ds_browseracceptheader'] = browser_info[:accept_header]
297
+ if (shipping_address = options[:shipping_address])
298
+ post[:'3ds_shipaddrstate'] = shipping_address[:state]
299
+ post[:'3ds_shipaddrpostcode'] = shipping_address[:zip]
300
+ post[:'3ds_shipaddrline2'] = shipping_address[:address2]
301
+ post[:'3ds_shipaddrline1'] = shipping_address[:address1]
302
+ post[:'3ds_shipaddrcountry'] = shipping_address[:country]
303
+ post[:'3ds_shipaddrcity'] = shipping_address[:city]
304
+ end
305
+ elsif options[:three_d_secure]
306
+ add_normalized_3d_secure_2_data(post, options)
307
+ end
308
+ end
309
+
310
+ def add_3d_secure_1_data(post, options)
311
+ post[:i8] = build_i8(options[:eci], options[:cavv], options[:xid])
312
+ end
313
+
314
+ def add_normalized_3d_secure_2_data(post, options)
315
+ three_d_secure_options = options[:three_d_secure]
316
+
317
+ post[:i8] = build_i8(
318
+ three_d_secure_options[:eci],
319
+ three_d_secure_options[:cavv]
320
+ )
321
+ post[:'3ds_version'] = three_d_secure_options[:version]
322
+ post[:'3ds_dstrxid'] = three_d_secure_options[:ds_transaction_id]
323
+ end
324
+
325
+ def build_i8(eci, cavv=nil, xid=nil)
326
+ "#{eci}:#{cavv || 'none'}:#{xid || 'none'}"
269
327
  end
270
328
 
271
329
  def add_echo(post, options)
@@ -280,6 +338,12 @@ module ActiveMerchant #:nodoc:
280
338
 
281
339
  def add_transaction_type(post, options)
282
340
  post[:a9] = options[:transaction_type] if options[:transaction_type]
341
+ post[:a2] = '3' if options.dig(:metadata, :manual_entry)
342
+ end
343
+
344
+ def add_processor(post, options)
345
+ post[:r1] = options[:processor] || 'CREDORAX'
346
+ post[:r2] = options[:processor_merchant_id] if options[:processor_merchant_id]
283
347
  end
284
348
 
285
349
  ACTIONS = {
@@ -291,7 +355,8 @@ module ActiveMerchant #:nodoc:
291
355
  credit: '6',
292
356
  purchase_void: '7',
293
357
  refund_void: '8',
294
- capture_void: '9'
358
+ capture_void: '9',
359
+ threeds_completion: '92'
295
360
  }
296
361
 
297
362
  def commit(action, params, reference_action = nil)
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  # Initial setup instructions can be found in
4
- # http://cybersource.com/support_center/implementation/downloads/soap_api/SOAP_toolkits.pdf
4
+ # http://apps.cybersource.com/library/documentation/dev_guides/SOAP_Toolkits/SOAP_toolkits.pdf
5
5
  #
6
6
  # Important Notes
7
7
  # * For checks you can purchase and store.
@@ -24,10 +24,12 @@ module ActiveMerchant #:nodoc:
24
24
  self.test_url = 'https://ics2wstesta.ic3.com/commerce/1.x/transactionProcessor'
25
25
  self.live_url = 'https://ics2wsa.ic3.com/commerce/1.x/transactionProcessor'
26
26
 
27
- XSD_VERSION = '1.153'
27
+ # Schema files can be found here: https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/
28
+ TEST_XSD_VERSION = '1.159'
29
+ PRODUCTION_XSD_VERSION = '1.159'
28
30
 
29
31
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :dankort, :maestro, :elo]
30
- self.supported_countries = %w(US BR CA CN DK FI FR DE IN JP MX NO SE GB SG LB)
32
+ self.supported_countries = %w(US BR CA CN DK FI FR DE IN JP MX NO SE GB SG LB PK)
31
33
 
32
34
  self.default_currency = 'USD'
33
35
  self.currencies_without_fractions = %w(JPY)
@@ -256,6 +258,7 @@ module ActiveMerchant #:nodoc:
256
258
  def build_auth_request(money, creditcard_or_reference, options)
257
259
  xml = Builder::XmlMarkup.new :indent => 2
258
260
  add_payment_method_or_subscription(xml, money, creditcard_or_reference, options)
261
+ add_threeds_2_ucaf_data(xml, creditcard_or_reference, options)
259
262
  add_decision_manager_fields(xml, options)
260
263
  add_mdd_fields(xml, options)
261
264
  add_auth_service(xml, creditcard_or_reference, options)
@@ -263,6 +266,8 @@ module ActiveMerchant #:nodoc:
263
266
  add_payment_network_token(xml) if network_tokenization?(creditcard_or_reference)
264
267
  add_business_rules_data(xml, creditcard_or_reference, options)
265
268
  add_stored_credential_options(xml, options)
269
+ add_issuer_additional_data(xml, options)
270
+
266
271
  xml.target!
267
272
  end
268
273
 
@@ -291,6 +296,7 @@ module ActiveMerchant #:nodoc:
291
296
  def build_purchase_request(money, payment_method_or_reference, options)
292
297
  xml = Builder::XmlMarkup.new :indent => 2
293
298
  add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
299
+ add_threeds_2_ucaf_data(xml, payment_method_or_reference, options)
294
300
  add_decision_manager_fields(xml, options)
295
301
  add_mdd_fields(xml, options)
296
302
  if !payment_method_or_reference.is_a?(String) && card_brand(payment_method_or_reference) == 'check'
@@ -301,6 +307,8 @@ module ActiveMerchant #:nodoc:
301
307
  add_payment_network_token(xml) if network_tokenization?(payment_method_or_reference)
302
308
  add_business_rules_data(xml, payment_method_or_reference, options) unless options[:pinless_debit_card]
303
309
  end
310
+ add_issuer_additional_data(xml, options)
311
+
304
312
  xml.target!
305
313
  end
306
314
 
@@ -485,6 +493,14 @@ module ActiveMerchant #:nodoc:
485
493
  end
486
494
  end
487
495
 
496
+ def add_issuer_additional_data(xml, options)
497
+ return unless options[:issuer_additional_data]
498
+
499
+ xml.tag! 'issuer' do
500
+ xml.tag! 'additionalData', options[:issuer_additional_data]
501
+ end
502
+ end
503
+
488
504
  def add_mdd_fields(xml, options)
489
505
  return unless options.keys.any? { |key| key.to_s.start_with?('mdd_field') }
490
506
 
@@ -516,19 +532,41 @@ module ActiveMerchant #:nodoc:
516
532
  add_auth_network_tokenization(xml, payment_method, options)
517
533
  else
518
534
  xml.tag! 'ccAuthService', {'run' => 'true'} do
519
- check_for_stored_cred_commerce_indicator(xml, options)
535
+ if options[:three_d_secure]
536
+ add_normalized_threeds_2_data(xml, payment_method, options)
537
+ else
538
+ indicator = options[:commerce_indicator] || stored_credential_commerce_indicator(options)
539
+ xml.tag!('commerceIndicator', indicator) if indicator
540
+ end
520
541
  end
521
542
  end
522
543
  end
523
544
 
524
- def check_for_stored_cred_commerce_indicator(xml, options)
525
- return unless options[:stored_credential]
526
- if commerce_indicator(options)
527
- xml.tag!('commerceIndicator', commerce_indicator(options))
545
+ def add_normalized_threeds_2_data(xml, payment_method, options)
546
+ threeds_2_options = options[:three_d_secure]
547
+
548
+ xml.tag!('cavv', threeds_2_options[:cavv]) if threeds_2_options[:cavv] && card_brand(payment_method).to_sym != :master
549
+ xml.tag!('cavvAlgorithm', threeds_2_options[:cavv_algorithm]) if threeds_2_options[:cavv_algorithm]
550
+ xml.tag!('paSpecificationVersion', threeds_2_options[:version]) if threeds_2_options[:version]
551
+ xml.tag!('directoryServerTransactionID', threeds_2_options[:ds_transaction_id]) if threeds_2_options[:ds_transaction_id]
552
+ xml.tag!('commerceIndicator', options[:commerce_indicator]) if options[:commerce_indicator]
553
+ xml.tag!('eciRaw', threeds_2_options[:eci]) if threeds_2_options[:eci]
554
+ xml.tag!('xid', threeds_2_options[:xid]) if threeds_2_options[:xid]
555
+ xml.tag!('veresEnrolled', threeds_2_options[:enrolled]) if threeds_2_options[:enrolled]
556
+ xml.tag!('paresStatus', threeds_2_options[:authentication_response_status]) if threeds_2_options[:authentication_response_status]
557
+ end
558
+
559
+ def add_threeds_2_ucaf_data(xml, payment_method, options)
560
+ return unless options[:three_d_secure] && card_brand(payment_method).to_sym == :master
561
+
562
+ xml.tag! 'ucaf' do
563
+ xml.tag!('authenticationData', options[:three_d_secure][:cavv])
564
+ xml.tag!('collectionIndicator', options[:collection_indicator]) if options[:collection_indicator]
528
565
  end
529
566
  end
530
567
 
531
- def commerce_indicator(options)
568
+ def stored_credential_commerce_indicator(options)
569
+ return unless options[:stored_credential]
532
570
  return if options[:stored_credential][:initial_transaction]
533
571
  case options[:stored_credential][:reason_type]
534
572
  when 'installment' then 'install'
@@ -550,7 +588,7 @@ module ActiveMerchant #:nodoc:
550
588
  xml.tag!('commerceIndicator', 'vbv')
551
589
  xml.tag!('xid', payment_method.payment_cryptogram)
552
590
  end
553
- when :mastercard
591
+ when :master
554
592
  xml.tag! 'ucaf' do
555
593
  xml.tag!('authenticationData', payment_method.payment_cryptogram)
556
594
  xml.tag!('collectionIndicator', '2')
@@ -713,6 +751,8 @@ module ActiveMerchant #:nodoc:
713
751
 
714
752
  # Where we actually build the full SOAP request using builder
715
753
  def build_request(body, options)
754
+ xsd_version = test? ? TEST_XSD_VERSION : PRODUCTION_XSD_VERSION
755
+
716
756
  xml = Builder::XmlMarkup.new :indent => 2
717
757
  xml.instruct!
718
758
  xml.tag! 's:Envelope', {'xmlns:s' => 'http://schemas.xmlsoap.org/soap/envelope/'} do
@@ -725,7 +765,7 @@ module ActiveMerchant #:nodoc:
725
765
  end
726
766
  end
727
767
  xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
728
- xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{XSD_VERSION}"} do
768
+ xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{xsd_version}"} do
729
769
  add_merchant_data(xml, options)
730
770
  xml << body
731
771
  end
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
 
7
7
  self.supported_countries = ['AR', 'BR', 'CL', 'CO', 'MX', 'PE', 'UY', 'TR']
8
8
  self.default_currency = 'USD'
9
- self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
9
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro, :naranja, :cabal]
10
10
 
11
11
  self.homepage_url = 'https://dlocal.com/'
12
12
  self.display_name = 'dLocal'
@@ -0,0 +1,245 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class DecidirGateway < Gateway
4
+ self.test_url = 'https://developers.decidir.com/api/v2'
5
+ self.live_url = 'https://live.decidir.com/api/v2'
6
+
7
+ self.supported_countries = ['AR']
8
+ self.money_format = :cents
9
+ self.default_currency = 'ARS'
10
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :naranja, :cabal]
11
+
12
+ self.homepage_url = 'http://www.decidir.com'
13
+ self.display_name = 'Decidir'
14
+
15
+ STANDARD_ERROR_CODE_MAPPING = {
16
+ 1 => STANDARD_ERROR_CODE[:call_issuer],
17
+ 2 => STANDARD_ERROR_CODE[:call_issuer],
18
+ 3 => STANDARD_ERROR_CODE[:config_error],
19
+ 4 => STANDARD_ERROR_CODE[:pickup_card],
20
+ 5 => STANDARD_ERROR_CODE[:card_declined],
21
+ 7 => STANDARD_ERROR_CODE[:pickup_card],
22
+ 12 => STANDARD_ERROR_CODE[:processing_error],
23
+ 14 => STANDARD_ERROR_CODE[:invalid_number],
24
+ 28 => STANDARD_ERROR_CODE[:processing_error],
25
+ 38 => STANDARD_ERROR_CODE[:incorrect_pin],
26
+ 39 => STANDARD_ERROR_CODE[:invalid_number],
27
+ 43 => STANDARD_ERROR_CODE[:pickup_card],
28
+ 45 => STANDARD_ERROR_CODE[:card_declined],
29
+ 46 => STANDARD_ERROR_CODE[:invalid_number],
30
+ 47 => STANDARD_ERROR_CODE[:card_declined],
31
+ 48 => STANDARD_ERROR_CODE[:card_declined],
32
+ 49 => STANDARD_ERROR_CODE[:invalid_expiry_date],
33
+ 51 => STANDARD_ERROR_CODE[:card_declined],
34
+ 53 => STANDARD_ERROR_CODE[:card_declined],
35
+ 54 => STANDARD_ERROR_CODE[:expired_card],
36
+ 55 => STANDARD_ERROR_CODE[:incorrect_pin],
37
+ 56 => STANDARD_ERROR_CODE[:card_declined],
38
+ 57 => STANDARD_ERROR_CODE[:card_declined],
39
+ 76 => STANDARD_ERROR_CODE[:call_issuer],
40
+ 96 => STANDARD_ERROR_CODE[:processing_error],
41
+ 97 => STANDARD_ERROR_CODE[:processing_error],
42
+ }
43
+
44
+ def initialize(options={})
45
+ requires!(options, :api_key)
46
+ super
47
+ @options[:preauth_mode] ||= false
48
+ end
49
+
50
+ def purchase(money, payment, options={})
51
+ raise ArgumentError, 'Purchase is not supported on Decidir gateways configured with the preauth_mode option' if @options[:preauth_mode]
52
+
53
+ post = {}
54
+ add_auth_purchase_params(post, money, payment, options)
55
+ commit(:post, 'payments', post)
56
+ end
57
+
58
+ def authorize(money, payment, options={})
59
+ raise ArgumentError, 'Authorize is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
60
+
61
+ post = {}
62
+ add_auth_purchase_params(post, money, payment, options)
63
+ commit(:post, 'payments', post)
64
+ end
65
+
66
+ def capture(money, authorization, options={})
67
+ raise ArgumentError, 'Capture is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
68
+
69
+ post = {}
70
+ add_amount(post, money, options)
71
+ commit(:put, "payments/#{authorization}", post)
72
+ end
73
+
74
+ def refund(money, authorization, options={})
75
+ post = {}
76
+ add_amount(post, money, options)
77
+ commit(:post, "payments/#{authorization}/refunds", post)
78
+ end
79
+
80
+ def void(authorization, options={})
81
+ post = {}
82
+ commit(:post, "payments/#{authorization}/refunds", post)
83
+ end
84
+
85
+ def verify(credit_card, options={})
86
+ raise ArgumentError, 'Verify is not supported on Decidir gateways unless the preauth_mode option is enabled' unless @options[:preauth_mode]
87
+
88
+ MultiResponse.run(:use_first_response) do |r|
89
+ r.process { authorize(100, credit_card, options) }
90
+ r.process(:ignore_result) { void(r.authorization, options) }
91
+ end
92
+ end
93
+
94
+ def supports_scrubbing?
95
+ true
96
+ end
97
+
98
+ def scrub(transcript)
99
+ transcript.
100
+ gsub(%r((apikey: )\w+)i, '\1[FILTERED]').
101
+ gsub(%r((\"card_number\\\":\\\")\d+), '\1[FILTERED]').
102
+ gsub(%r((\"security_code\\\":\\\")\d+), '\1[FILTERED]').
103
+ gsub(%r((\"emv_issuer_data\\\":\\\")\d+), '\1[FILTERED]')
104
+ end
105
+
106
+ private
107
+
108
+ def add_auth_purchase_params(post, money, credit_card, options)
109
+ post[:payment_method_id] = add_payment_method_id(credit_card)
110
+ post[:site_transaction_id] = options[:order_id]
111
+ post[:bin] = credit_card.number[0..5]
112
+ post[:payment_type] = options[:payment_type] || 'single'
113
+ post[:installments] = options[:installments] ? options[:installments].to_i : 1
114
+ post[:description] = options[:description] if options[:description]
115
+ post[:email] = options[:email] if options[:email]
116
+ post[:sub_payments] = []
117
+
118
+ add_invoice(post, money, options)
119
+ add_payment(post, credit_card, options)
120
+ end
121
+
122
+ def add_payment_method_id(credit_card)
123
+ if options[:payment_method_id]
124
+ options[:payment_method_id].to_i
125
+ elsif CreditCard.brand?(credit_card.number) == 'cabal'
126
+ 63
127
+ elsif CreditCard.brand?(credit_card.number) == 'naranja'
128
+ 24
129
+ else
130
+ 1
131
+ end
132
+ end
133
+
134
+ def add_invoice(post, money, options)
135
+ add_amount(post, money, options)
136
+ post[:currency] = (options[:currency] || currency(money))
137
+ end
138
+
139
+ def add_amount(post, money, options)
140
+ currency = (options[:currency] || currency(money))
141
+ post[:amount] = localized_amount(money, currency).to_i
142
+ end
143
+
144
+ def add_payment(post, credit_card, options)
145
+ card_data = {}
146
+ card_data[:card_number] = credit_card.number
147
+ card_data[:card_expiration_month] = format(credit_card.month, :two_digits)
148
+ card_data[:card_expiration_year] = format(credit_card.year, :two_digits)
149
+ card_data[:security_code] = credit_card.verification_value if credit_card.verification_value?
150
+ card_data[:card_holder_name] = credit_card.name if credit_card.name
151
+
152
+ # additional data used for Visa transactions
153
+ card_data[:card_holder_door_number] = options[:card_holder_door_number].to_i if options[:card_holder_door_number]
154
+ card_data[:card_holder_birthday] = options[:card_holder_birthday] if options[:card_holder_birthday]
155
+
156
+ card_data[:card_holder_identification] = {}
157
+ card_data[:card_holder_identification][:type] = options[:card_holder_identification_type] if options[:card_holder_identification_type]
158
+ card_data[:card_holder_identification][:number] = options[:card_holder_identification_number] if options[:card_holder_identification_number]
159
+
160
+ post[:card_data] = card_data
161
+ end
162
+
163
+ def headers(options = {})
164
+ {
165
+ 'apikey' => @options[:api_key],
166
+ 'Content-type' => 'application/json',
167
+ 'Cache-Control' => 'no-cache'
168
+ }
169
+ end
170
+
171
+ def commit(method, endpoint, parameters, options={})
172
+ url = "#{(test? ? test_url : live_url)}/#{endpoint}"
173
+
174
+ begin
175
+ raw_response = ssl_request(method, url, post_data(parameters), headers(options))
176
+ response = parse(raw_response)
177
+ rescue ResponseError => e
178
+ raw_response = e.response.body
179
+ response = parse(raw_response)
180
+ end
181
+
182
+ success = success_from(response)
183
+ Response.new(
184
+ success,
185
+ message_from(success, response),
186
+ response,
187
+ authorization: authorization_from(response),
188
+ test: test?,
189
+ error_code: success ? nil : error_code_from(response)
190
+ )
191
+ end
192
+
193
+ def post_data(parameters = {})
194
+ parameters.to_json
195
+ end
196
+
197
+ def parse(body)
198
+ JSON.parse(body)
199
+ rescue JSON::ParserError
200
+ {
201
+ 'message' => "A non-JSON response was received from Decidir where one was expected. The raw response was:\n\n#{body}"
202
+ }
203
+ end
204
+
205
+ def message_from(success, response)
206
+ return response['status'] if success
207
+ return response['message'] if response['message']
208
+
209
+ message = nil
210
+
211
+ if error = response.dig('status_details', 'error')
212
+ message = error.dig('reason', 'description')
213
+ elsif response['error_type']
214
+ if response['validation_errors']
215
+ message = response['validation_errors'].map { |errors| "#{errors['code']}: #{errors['param']}" }.join(', ')
216
+ end
217
+ message ||= response['error_type']
218
+ end
219
+
220
+ message
221
+ end
222
+
223
+ def success_from(response)
224
+ response['status'] == 'approved' || response['status'] == 'pre_approved'
225
+ end
226
+
227
+ def authorization_from(response)
228
+ response['id']
229
+ end
230
+
231
+ def error_code_from(response)
232
+ error_code = nil
233
+ if error = response.dig('status_details', 'error')
234
+ code = error.dig('reason', 'id')
235
+ error_code = STANDARD_ERROR_CODE_MAPPING[code]
236
+ error_code ||= error['type']
237
+ elsif response['error_type']
238
+ error_code = response['error_type'] if response['validation_errors']
239
+ end
240
+
241
+ error_code || STANDARD_ERROR_CODE[:processing_error]
242
+ end
243
+ end
244
+ end
245
+ end