activemerchant 1.124.0 → 1.125.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +85 -0
  3. data/lib/active_merchant/billing/check.rb +5 -8
  4. data/lib/active_merchant/billing/credit_card.rb +10 -0
  5. data/lib/active_merchant/billing/gateway.rb +1 -1
  6. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  7. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
  8. data/lib/active_merchant/billing/gateways/cyber_source.rb +11 -3
  9. data/lib/active_merchant/billing/gateways/d_local.rb +4 -5
  10. data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
  11. data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
  12. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  13. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  14. data/lib/active_merchant/billing/gateways/global_collect.rb +106 -16
  15. data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
  16. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  17. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  18. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  19. data/lib/active_merchant/billing/gateways/nmi.rb +2 -1
  20. data/lib/active_merchant/billing/gateways/orbital.rb +17 -2
  21. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  22. data/lib/active_merchant/billing/gateways/payflow.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/paymentez.rb +9 -2
  24. data/lib/active_merchant/billing/gateways/paysafe.rb +41 -5
  25. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -1
  26. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  27. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  28. data/lib/active_merchant/billing/gateways/priority.rb +347 -0
  29. data/lib/active_merchant/billing/gateways/safe_charge.rb +1 -0
  30. data/lib/active_merchant/billing/gateways/stripe.rb +20 -10
  31. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +37 -3
  32. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  33. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  34. data/lib/active_merchant/billing/gateways/worldpay.rb +181 -64
  35. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  36. data/lib/active_merchant/version.rb +1 -1
  37. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6af1ccdc1542e628e6825023b1d26d67177e6630e08b6b07896772bcf2ddde7b
4
- data.tar.gz: ebeb390672f779ee8e660f5bac6baab8423fab0e4d8fb89d2ef626f5f5086c6f
3
+ metadata.gz: 9f8d0ad7bb54e228b44738e6cf324034467970e6709047a7f799dd7d3276998c
4
+ data.tar.gz: 96ab0828793d536f7df925fe962444c1cc0c48d129de54c19d40c81145ed1d11
5
5
  SHA512:
6
- metadata.gz: a64e4885c13f8b95907d15058b6644b03f2baff32a5df8e54ea1d5ee968df7f50455f7dcab3cd07d5acacd7e940504bc728dfa9b39874b9f57ba7d9011617e38
7
- data.tar.gz: dd867effc34ba7939ac9dbacfa5583ea199dd3eb1387e34c57101a5b9ef282d2562fe4b564109db6c5157fca5cc25c0981deec960a06c64d66ab3861f0d5df9d
6
+ metadata.gz: 1a31f43e0702edeb5727c61285c54afeecc5accc6c9f89134966228fe9c08369f9918890ff25cda85ec863f3cda2bd1e2e6dde8f4eed1254ed6d520f37067dcd
7
+ data.tar.gz: 144dc89e223d28ceed1399b50d15116d169dc056f878bb9ba9ed477e4c141fde2c2bc25a882610e4295fa3a25fc8f63800034352b77eb3a370718ea64d017665
data/CHANGELOG CHANGED
@@ -1,7 +1,92 @@
1
+
1
2
  = ActiveMerchant CHANGELOG
2
3
 
3
4
  == HEAD
4
5
 
6
+ == Version 1.125.0 (January 20, 2022)
7
+ * Wompi: support gateway [therufs] #4173
8
+ * Stripe Payment Intents: Add setup_purchase [aenand] #4178
9
+ * Ipg: Add new gateway [ajawadmirza] #4171
10
+ * Worldpay: Adding support for google pay and apple pay [cristian] #4180
11
+ * Worldpay: Adding scrubbing for network token transactions [cristian] #4181
12
+ * SafeCharge: Add sg_NotUseCVV field [ajawadmirza] #4177
13
+ * PayULatam: Correctly map maestro and condensa card types [dsmcclain] #4182
14
+ * StripePaymentIntents: Refactor response for setup_purchase [aenand] #4183
15
+ * Wompi: cast error messages to JSON [therufs] #4186
16
+ * NMI: Omit initial_transaction_id for CIT [aenand] #4189
17
+ * Priority: Support Priority Payment Systems gateway [jessiagee] #4166
18
+ * GlobalCollect: Support for Lodging Data [naashton] #4190
19
+ * IPG: Add support for sub-merchant and recurring type fields [ajawadmirza] # 4188
20
+ * Wompi: Support `installments` option [therufs] #4192
21
+ * Stripe PI: add support for `fulfillment_date` and `event_type` [dsmcclain] #4193
22
+ * Paysafe: Adjust logic for sending 3DS field [meagabeth] #4194
23
+ * Priority: Fix unit test cases [ajawadmirza] #4195
24
+ * EBANX: New Gateway Specific Receiver [spreedly-kledoux] #4198
25
+ * Wompi: Don't send CVV field if no CVV provided [therufs] #4199
26
+ * Worldpay: cleaning order_id according to worldpay rules [cristian] #4197
27
+ * Paysafe: Concatenate credentials for headers [meagabeth] #4201
28
+ * Stripe Payment Intents: Add metadata to setup_purchase [aenand] #4202
29
+ * Priority: Add gateway standard changes [ajawadmirza] #4200
30
+ * IPG: Add support for payment by token [ajawadmirza] #4191
31
+ * Element (Vantiv Express): Add support for general credit [dsmcclain] #4203
32
+ * Worldpay: Update supported countries list, currencies [jherreraa] #4207
33
+ * StripePI: Adding countries available. [gasb150] #4208
34
+ * Orbital: Adding google pay payment tests for Orbital. [ajawadmirza] #4205
35
+ * Bug: Fixing supported countries method when there is inheritance involved [cristian] #4211
36
+ * Mundipagg: Update success method [ajawadmirza] #4210
37
+ * Worldpay: Add support for Visa Direct Fast Funds Credit [dsmcclain] #4212
38
+ * Paysafe: Add support for stored credentials [meagabeth] #4214
39
+ * Worldpay: Adding missing countries to supported countries [cristian] #4213
40
+ * Update institution numbers for Canadian banks [therufs] #4216
41
+ * Worldpay: Set default eCommerce indicator for EMVCO network tokens [shasum] #4215
42
+ * Update handling routing numbers for Canadian banks [therufs] #4217
43
+ * Stripe: API version updated [jherreraa] #4209
44
+ * Mercado Pago: Update verify method [ajawadmirza] #4219
45
+ * DLocal: Set API Version [gasb150] #4222
46
+ * Wompi: Add support for Authorize and Capture [rachelkirk] #4218
47
+ * Priority: Update source and billing address checks [jessiagee] #4220
48
+ * Pin Payments: Add support for `diners_club`, `discover`, and `jcb` cardtypes [montdidier] #4142
49
+ * USA ePay: Add store method [ajawadmirza] #4224
50
+ * IPG: Quick fix to remove warning [ajawadmirza] #4225
51
+ * Remove YAML warning on load_fixtures_method [jherreraa] #4226
52
+ * Worldpay: Add support for tokenizing payment methods with transaction identifiers [dsmcclain] #4227
53
+ * USA ePay: Update implementation to send valid authorization [ajawadmirza] #4231
54
+ * USA ePay: Add store test, update authorize param [jessiagee] #4232
55
+ * Stripe: Update destination test account [jherreraa] #4234
56
+ * Add skip_response option on request check for commit stubs [cristian] #4223
57
+ * Pin Payments: Add support for `void` and New Zealand to supported countries. [montdidier] #4144
58
+ * Wompi: Update authorization in `capture` method. [rachelkirk] #4238
59
+ * IPG: Update authorization to support `store` method token. [ajawadmirza] #4233
60
+ * Paymentez: Update card mappings [ajawadmirza] #4237
61
+ * Priority: Update parsing for error messages [jessiagee] #4245
62
+ * GlobalCollect: Support for Airline Data [naashton] #4187
63
+ * IPG: Add `tpv_error_code` and `tpv_error_msg` fields [ajawadmirza] #4241
64
+ * StripePI: Set restriction for Apple/Google Pay [jherreraa] #4247
65
+ * Cashnet: support multiple itemcodes and amounts [peteroas] #4243
66
+ * IPG: Send default currency in `verify` and two digit `ExpMonth` [ajawadmirza] #4244
67
+ * Stripe: Add remote tests set up to avoid exceed the max external accounts limit [jherreraa] #4239
68
+ * Stripe: Add support for `radar_options: skip_rules` [dsmcclain] #4250
69
+ * CyberSource: Add `user_po`, `taxable`, `national_tax_indicator`, `tax_amount`, and `national_tax` fields [ajawadmirza] #4251
70
+ * Kushki: Add support for `metadata` [rachelkirk] #4253
71
+ * IPG: Add `redact` operation [ajawadmirza] #4254
72
+ * Wompi: Update sandbox and production endpoints [rachelkirk] #4255
73
+ * Orbital: Add `sca_merchant_initiated` operation [ajawadmirza] #4256
74
+ * Cashnet: convert amounts to integers for proper gateway handling [peteroas] #2207
75
+ * PayTrace: Add `unstore` operation [ajawadmirza] #4262
76
+ * Decidir Plus: Add gateway adapter [naashton] #4264
77
+ * CheckoutV2: Add support for Apple Pay and Google Pay tokens [AMHOL] #4235
78
+ * Decidir Plus: Update payment reference [naashton] #4271
79
+ * Paysafe: Update redact method [meagabeth] #4269
80
+ * CyberSource: Add `line_items` field in authorize method [ajawadmirza] #4268
81
+ * CheckoutV2: Support processing channel and marketplace sub entity ID [AMHOL] #4236
82
+ * Elavon: `third_party_token` bug fix [rachelkirk] #4273
83
+ * Decidir Plus: Add `sub_payments` field [naashton] #4274
84
+ * Pin Payments: Add `unstore` support [montdidier] #4276
85
+ * Orbital: Add support for $0 verify [javierpedrozaing] #4275
86
+ * Update inline documentation with all supported cardtypes [ali-hassan] #4283
87
+ * PayWay: Update endpoints, response code [jessiagee] #4281
88
+ * CyberSource: Add `line_items` for purchase [ajawadmirza] #4282
89
+
5
90
  == Version 1.124.0 (October 28th, 2021)
6
91
  * Worldpay: Add Support for Submerchant Data on Worldpay [almalee24] #4147
7
92
  * dlocal: Add device_id and ip to payer object and add additional_data [aenand] #4116
@@ -14,13 +14,13 @@ module ActiveMerchant #:nodoc:
14
14
  attr_accessor :institution_number, :transit_number
15
15
 
16
16
  # Canadian Institution Numbers
17
- # Found here: https://en.wikipedia.org/wiki/Routing_number_(Canada)
17
+ # Partial list found here: https://en.wikipedia.org/wiki/Routing_number_(Canada)
18
18
  CAN_INSTITUTION_NUMBERS = %w(
19
- 001 002 003 004 006 010 016 030 039 117 127 177 219 245 260 269 270 308
20
- 309 310 315 320 338 340 509 540 608 614 623 809 815 819 828 829 837 839
19
+ 001 002 003 004 006 010 016 030 039 117 127 177 219 245 260 269 270 308
20
+ 309 310 315 320 338 340 509 540 608 614 623 809 815 819 828 829 837 839
21
21
  865 879 889 899 241 242 248 250 265 275 277 290 294 301 303 307 311 314
22
22
  321 323 327 328 330 332 334 335 342 343 346 352 355 361 362 366 370 372
23
- 376 378 807 853
23
+ 376 378 807 853 890
24
24
  )
25
25
 
26
26
  def name
@@ -71,11 +71,8 @@ module ActiveMerchant #:nodoc:
71
71
  (7 * (digits[1] + digits[4] + digits[7])) +
72
72
  (digits[2] + digits[5] + digits[8])) % 10
73
73
 
74
- return checksum == 0
74
+ return checksum == 0 || CAN_INSTITUTION_NUMBERS.include?(routing_number[1..3])
75
75
  end
76
-
77
- return CAN_INSTITUTION_NUMBERS.include?(routing_number[0..2]) if digits.size == 8
78
-
79
76
  false
80
77
  end
81
78
  end
@@ -18,6 +18,11 @@ module ActiveMerchant #:nodoc:
18
18
  # * Dankort
19
19
  # * Maestro
20
20
  # * Forbrugsforeningen
21
+ # * Sodexo
22
+ # * Vr
23
+ # * Carnet
24
+ # * Synchrony
25
+ # * Routex
21
26
  # * Elo
22
27
  # * Alelo
23
28
  # * Cabal
@@ -97,6 +102,11 @@ module ActiveMerchant #:nodoc:
97
102
  # * +'dankort'+
98
103
  # * +'maestro'+
99
104
  # * +'forbrugsforeningen'+
105
+ # * +'sodexo'+
106
+ # * +'vr'+
107
+ # * +'carnet'+
108
+ # * +'synchrony'+
109
+ # * +'routex'+
100
110
  # * +'elo'+
101
111
  # * +'alelo'+
102
112
  # * +'cabal'+
@@ -162,7 +162,7 @@ module ActiveMerchant #:nodoc:
162
162
  end
163
163
 
164
164
  def self.supported_countries
165
- @supported_countries ||= []
165
+ @supported_countries ||= (self.superclass.supported_countries || [])
166
166
  end
167
167
 
168
168
  def supported_countries
@@ -41,7 +41,7 @@ module ActiveMerchant #:nodoc:
41
41
  def purchase(money, payment_object, options = {})
42
42
  post = {}
43
43
  add_creditcard(post, payment_object)
44
- add_invoice(post, options)
44
+ add_invoice(post, money, options)
45
45
  add_address(post, options)
46
46
  add_customer_data(post, options)
47
47
  commit('SALE', money, post)
@@ -50,7 +50,7 @@ module ActiveMerchant #:nodoc:
50
50
  def refund(money, identification, options = {})
51
51
  post = {}
52
52
  post[:origtx] = identification
53
- add_invoice(post, options)
53
+ add_invoice(post, money, options)
54
54
  add_customer_data(post, options)
55
55
  commit('REFUND', money, post)
56
56
  end
@@ -69,7 +69,6 @@ module ActiveMerchant #:nodoc:
69
69
  private
70
70
 
71
71
  def commit(action, money, fields)
72
- fields[:amount] = amount(money)
73
72
  url = (test? ? test_url : live_url) + CGI.escape(@options[:merchant_gateway_name])
74
73
  raw_response = ssl_post(url, post_data(action, fields))
75
74
  parsed_response = parse(raw_response)
@@ -92,6 +91,7 @@ module ActiveMerchant #:nodoc:
92
91
 
93
92
  def post_data(action, parameters = {})
94
93
  post = {}
94
+
95
95
  post[:command] = action
96
96
  post[:merchant] = @options[:merchant]
97
97
  post[:operator] = @options[:operator]
@@ -110,9 +110,19 @@ module ActiveMerchant #:nodoc:
110
110
  post[:lname] = creditcard.last_name
111
111
  end
112
112
 
113
- def add_invoice(post, options)
113
+ def add_invoice(post, money, options)
114
114
  post[:order_number] = options[:order_id] if options[:order_id].present?
115
- post[:itemcode] = (options[:item_code] || @options[:default_item_code])
115
+
116
+ if options[:item_codes].present?
117
+ codes_and_amounts = options[:item_codes].transform_keys { |key| key.to_s.delete('_') }
118
+ codes_and_amounts.each do |key, value|
119
+ post[key] = value if key.start_with?('itemcode')
120
+ post[key] = amount(value.to_i) if key.start_with?('amount')
121
+ end
122
+ else
123
+ post[:itemcode] = (options[:item_code] || @options[:default_item_code])
124
+ post[:amount] = amount(money.to_i)
125
+ end
116
126
  end
117
127
 
118
128
  def add_address(post, options)
@@ -87,6 +87,8 @@ module ActiveMerchant #:nodoc:
87
87
  add_transaction_data(post, options)
88
88
  add_3ds(post, options)
89
89
  add_metadata(post, options)
90
+ add_processing_channel(post, options)
91
+ add_marketplace_data(post, options)
90
92
  end
91
93
 
92
94
  def add_invoice(post, money, options)
@@ -109,12 +111,17 @@ module ActiveMerchant #:nodoc:
109
111
 
110
112
  def add_payment_method(post, payment_method, options)
111
113
  post[:source] = {}
112
- if payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :network_token
114
+ if payment_method.is_a?(NetworkTokenizationCreditCard)
115
+ token_type = token_type_from(payment_method)
116
+ cryptogram = payment_method.payment_cryptogram
117
+ eci = payment_method.eci || options[:eci]
118
+ eci ||= '05' if token_type == 'vts'
119
+
113
120
  post[:source][:type] = 'network_token'
114
121
  post[:source][:token] = payment_method.number
115
- post[:source][:token_type] = payment_method.brand == 'visa' ? 'vts' : 'mdes'
116
- post[:source][:cryptogram] = payment_method.payment_cryptogram
117
- post[:source][:eci] = options[:eci] || '05'
122
+ post[:source][:token_type] = token_type
123
+ post[:source][:cryptogram] = cryptogram if cryptogram
124
+ post[:source][:eci] = eci if eci
118
125
  else
119
126
  post[:source][:type] = 'card'
120
127
  post[:source][:name] = payment_method.name
@@ -186,6 +193,17 @@ module ActiveMerchant #:nodoc:
186
193
  end
187
194
  end
188
195
 
196
+ def add_processing_channel(post, options)
197
+ post[:processing_channel_id] = options[:processing_channel_id] if options[:processing_channel_id]
198
+ end
199
+
200
+ def add_marketplace_data(post, options)
201
+ if options[:marketplace]
202
+ post[:marketplace] = {}
203
+ post[:marketplace][:sub_entity_id] = options[:marketplace][:sub_entity_id] if options[:marketplace][:sub_entity_id]
204
+ end
205
+ end
206
+
189
207
  def commit(action, post, authorization = nil)
190
208
  begin
191
209
  raw_response = (action == :verify_payment ? ssl_get("#{base_url}/payments/#{post}", headers) : ssl_post(url(post, action, authorization), post.to_json, headers))
@@ -306,6 +324,17 @@ module ActiveMerchant #:nodoc:
306
324
  STANDARD_ERROR_CODE_MAPPING[response['response_code']]
307
325
  end
308
326
  end
327
+
328
+ def token_type_from(payment_method)
329
+ case payment_method.source
330
+ when :network_token
331
+ payment_method.brand == 'visa' ? 'vts' : 'mdes'
332
+ when :google_pay, :android_pay
333
+ 'googlepay'
334
+ when :apple_pay
335
+ 'applepay'
336
+ end
337
+ end
309
338
  end
310
339
  end
311
340
  end
@@ -500,6 +500,8 @@ module ActiveMerchant #:nodoc:
500
500
  end
501
501
 
502
502
  def add_line_item_data(xml, options)
503
+ return unless options[:line_items]
504
+
503
505
  options[:line_items].each_with_index do |value, index|
504
506
  xml.tag! 'item', { 'id' => index } do
505
507
  xml.tag! 'unitPrice', localized_amount(value[:declared_value].to_i, options[:currency] || default_currency)
@@ -507,6 +509,8 @@ module ActiveMerchant #:nodoc:
507
509
  xml.tag! 'productCode', value[:code] || 'shipping_only'
508
510
  xml.tag! 'productName', value[:description]
509
511
  xml.tag! 'productSKU', value[:sku]
512
+ xml.tag! 'taxAmount', value[:tax_amount] if value[:tax_amount]
513
+ xml.tag! 'nationalTax', value[:national_tax] if value[:national_tax]
510
514
  end
511
515
  end
512
516
  end
@@ -522,10 +526,12 @@ module ActiveMerchant #:nodoc:
522
526
  end
523
527
 
524
528
  def add_merchant_descriptor(xml, options)
525
- return unless options[:merchant_descriptor]
529
+ return unless options[:merchant_descriptor] || options[:user_po] || options[:taxable]
526
530
 
527
531
  xml.tag! 'invoiceHeader' do
528
- xml.tag! 'merchantDescriptor', options[:merchant_descriptor]
532
+ xml.tag! 'merchantDescriptor', options[:merchant_descriptor] if options[:merchant_descriptor]
533
+ xml.tag! 'userPO', options[:user_po] if options[:user_po]
534
+ xml.tag! 'taxable', options[:taxable] if options[:taxable]
529
535
  end
530
536
  end
531
537
 
@@ -628,11 +634,12 @@ module ActiveMerchant #:nodoc:
628
634
  end
629
635
 
630
636
  def add_other_tax(xml, options)
631
- return unless options[:local_tax_amount] || options[:national_tax_amount]
637
+ return unless options[:local_tax_amount] || options[:national_tax_amount] || options[:national_tax_indicator]
632
638
 
633
639
  xml.tag! 'otherTax' do
634
640
  xml.tag! 'localTaxAmount', options[:local_tax_amount] if options[:local_tax_amount]
635
641
  xml.tag! 'nationalTaxAmount', options[:national_tax_amount] if options[:national_tax_amount]
642
+ xml.tag! 'nationalTaxIndicator', options[:national_tax_indicator] if options[:national_tax_indicator]
636
643
  end
637
644
  end
638
645
 
@@ -887,6 +894,7 @@ module ActiveMerchant #:nodoc:
887
894
  else
888
895
  add_address(xml, payment_method_or_reference, options[:billing_address], options)
889
896
  add_address(xml, payment_method_or_reference, options[:shipping_address], options, true)
897
+ add_line_item_data(xml, options)
890
898
  add_purchase_data(xml, money, true, options)
891
899
  add_installments(xml, options)
892
900
  add_creditcard(xml, payment_method_or_reference)
@@ -26,6 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
  def authorize(money, payment, options = {})
27
27
  post = {}
28
28
  add_auth_purchase_params(post, money, payment, 'authorize', options)
29
+ post[:card][:verify] = true if options[:verify].to_s == 'true'
29
30
 
30
31
  commit('authorize', post, options)
31
32
  end
@@ -52,10 +53,7 @@ module ActiveMerchant #:nodoc:
52
53
  end
53
54
 
54
55
  def verify(credit_card, options = {})
55
- MultiResponse.run(:use_first_response) do |r|
56
- r.process { authorize(100, credit_card, options) }
57
- r.process(:ignore_result) { void(r.authorization, options) }
58
- end
56
+ authorize(0, credit_card, options.merge(verify: 'true'))
59
57
  end
60
58
 
61
59
  def supports_scrubbing?
@@ -191,7 +189,7 @@ module ActiveMerchant #:nodoc:
191
189
  def success_from(action, response)
192
190
  return false unless response['status_code']
193
191
 
194
- %w[100 200 400 600].include? response['status_code'].to_s
192
+ %w[100 200 400 600 700].include? response['status_code'].to_s
195
193
  end
196
194
 
197
195
  def message_from(action, response)
@@ -235,6 +233,7 @@ module ActiveMerchant #:nodoc:
235
233
  'X-Date' => timestamp,
236
234
  'X-Login' => @options[:login],
237
235
  'X-Trans-Key' => @options[:trans_key],
236
+ 'X-Version' => '2.1',
238
237
  'Authorization' => signature(post, timestamp)
239
238
  }
240
239
  headers.merge('X-Idempotency-Key' => options[:idempotency_key]) if options[:idempotency_key]
@@ -0,0 +1,173 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class DecidirPlusGateway < 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.default_currency = 'ARS'
9
+ self.supported_cardtypes = %i[visa master american_express discover]
10
+
11
+ self.homepage_url = 'http://decidir.com.ar/home'
12
+ self.display_name = 'Decidir Plus'
13
+
14
+ def initialize(options = {})
15
+ requires!(options, :public_key, :private_key)
16
+ super
17
+ end
18
+
19
+ def purchase(money, payment, options = {})
20
+ post = {}
21
+
22
+ add_payment(post, payment, options)
23
+ add_purchase_data(post, money, payment, options)
24
+
25
+ commit(:post, 'payments', post)
26
+ end
27
+
28
+ def refund(money, authorization, options = {})
29
+ post = {}
30
+ post[:amount] = money
31
+
32
+ commit(:post, "payments/#{add_reference(authorization)}/refunds")
33
+ end
34
+
35
+ def store(payment, options = {})
36
+ post = {}
37
+ add_payment(post, payment, options)
38
+
39
+ commit(:post, 'tokens', post)
40
+ end
41
+
42
+ def supports_scrubbing?
43
+ true
44
+ end
45
+
46
+ def scrub(transcript)
47
+ transcript.
48
+ gsub(%r((Apikey: )\w+), '\1[FILTERED]').
49
+ gsub(%r(("card_number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
50
+ gsub(%r(("security_code\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
51
+ end
52
+
53
+ private
54
+
55
+ def add_reference(authorization)
56
+ return unless authorization
57
+
58
+ authorization.split('|')[0]
59
+ end
60
+
61
+ def add_payment(post, payment, options = {})
62
+ if payment.is_a?(String)
63
+ token, bin = payment.split('|')
64
+ post[:token] = token
65
+ post[:bin] = bin
66
+ else
67
+ post[:card_number] = payment.number
68
+ post[:card_expiration_month] = payment.month.to_s.rjust(2, '0')
69
+ post[:card_expiration_year] = payment.year.to_s[-2..-1]
70
+ post[:security_code] = payment.verification_value.to_s
71
+ post[:card_holder_name] = payment.name
72
+ post[:card_holder_identification] = {}
73
+ post[:card_holder_identification][:type] = options[:dni]
74
+ post[:card_holder_identification][:number] = options[:card_holder_identification_number]
75
+ end
76
+ end
77
+
78
+ def add_purchase_data(post, money, payment, options = {})
79
+ post[:site_transaction_id] = options[:site_transaction_id] || SecureRandom.hex
80
+ post[:payment_method_id] = 1
81
+ post[:amount] = money
82
+ post[:currency] = options[:currency] || self.default_currency
83
+ post[:installments] = options[:installments] || 1
84
+ post[:payment_type] = options[:payment_type] || 'single'
85
+ add_sub_payments(post, options)
86
+ end
87
+
88
+ def add_sub_payments(post, options)
89
+ # sub_payments field is required for purchase transactions, even if empty
90
+ post[:sub_payments] = []
91
+
92
+ return unless sub_payments = options[:sub_payments]
93
+
94
+ sub_payments.each do |sub_payment|
95
+ sub_payment_hash = {
96
+ site_id: sub_payment[:site_id],
97
+ installments: sub_payment[:installments],
98
+ amount: sub_payment[:amount]
99
+ }
100
+ post[:sub_payments] << sub_payment_hash
101
+ end
102
+ end
103
+
104
+ def parse(body)
105
+ JSON.parse(body)
106
+ end
107
+
108
+ def commit(method, endpoint, parameters = {}, options = {})
109
+ begin
110
+ raw_response = ssl_request(method, url(endpoint), post_data(parameters), headers(endpoint))
111
+ response = parse(raw_response)
112
+ rescue ResponseError => e
113
+ raw_response = e.response.body
114
+ response = parse(raw_response)
115
+ end
116
+
117
+ Response.new(
118
+ success_from(response),
119
+ message_from(response),
120
+ response,
121
+ authorization: authorization_from(response),
122
+ avs_result: AVSResult.new(code: response['some_avs_response_key']),
123
+ cvv_result: CVVResult.new(response['some_cvv_response_key']),
124
+ test: test?,
125
+ error_code: error_code_from(response)
126
+ )
127
+ end
128
+
129
+ def headers(endpoint)
130
+ {
131
+ 'Content-Type' => 'application/json',
132
+ 'apikey' => endpoint == 'tokens' ? @options[:public_key] : @options[:private_key]
133
+ }
134
+ end
135
+
136
+ def url(action, options = {})
137
+ base_url = (test? ? test_url : live_url)
138
+
139
+ return "#{base_url}/#{action}"
140
+ end
141
+
142
+ def success_from(response)
143
+ response.dig('status') == 'approved' || response.dig('status') == 'active'
144
+ end
145
+
146
+ def message_from(response)
147
+ response.dig('status') || error_message(response) || response.dig('message')
148
+ end
149
+
150
+ def authorization_from(response)
151
+ return nil unless response.dig('id') || response.dig('bin')
152
+
153
+ "#{response.dig('id')}|#{response.dig('bin')}"
154
+ end
155
+
156
+ def post_data(parameters = {})
157
+ parameters.to_json
158
+ end
159
+
160
+ def error_code_from(response)
161
+ response.dig('error_type') unless success_from(response)
162
+ end
163
+
164
+ def error_message(response)
165
+ return error_code_from(response) unless validation_errors = response.dig('validation_errors')
166
+
167
+ validation_errors = validation_errors[0]
168
+
169
+ "#{validation_errors.dig('code')}: #{validation_errors.dig('param')}"
170
+ end
171
+ end
172
+ end
173
+ end
@@ -214,6 +214,7 @@ module ActiveMerchant #:nodoc:
214
214
  post[:metadata] = options[:metadata] if options[:metadata]
215
215
  post[:metadata] = {} if post[:metadata].nil?
216
216
  post[:metadata][:merchant_payment_code] = options[:order_id] if options[:order_id]
217
+ post[:processing_type] = options[:processing_type] if options[:processing_type]
217
218
  end
218
219
 
219
220
  def parse(body)
@@ -222,7 +223,8 @@ module ActiveMerchant #:nodoc:
222
223
 
223
224
  def commit(action, parameters)
224
225
  url = url_for((test? ? test_url : live_url), action, parameters)
225
- response = parse(ssl_request(HTTP_METHOD[action], url, post_data(action, parameters), { 'x-ebanx-client-user-agent': "ActiveMerchant/#{ActiveMerchant::VERSION}" }))
226
+
227
+ response = parse(ssl_request(HTTP_METHOD[action], url, post_data(action, parameters), headers(parameters)))
226
228
 
227
229
  success = success_from(action, response)
228
230
 
@@ -236,6 +238,19 @@ module ActiveMerchant #:nodoc:
236
238
  )
237
239
  end
238
240
 
241
+ def headers(params)
242
+ processing_type = params[:processing_type]
243
+ commit_headers = { 'x-ebanx-client-user-agent': "ActiveMerchant/#{ActiveMerchant::VERSION}" }
244
+
245
+ add_processing_type_to_commit_headers(commit_headers, processing_type) if processing_type == 'local'
246
+
247
+ commit_headers
248
+ end
249
+
250
+ def add_processing_type_to_commit_headers(commit_headers, processing_type)
251
+ commit_headers['x-ebanx-api-processing-type'] = processing_type
252
+ end
253
+
239
254
  def success_from(action, response)
240
255
  if %i[purchase capture refund].include?(action)
241
256
  response.try(:[], 'payment').try(:[], 'status') == 'CO'
@@ -298,7 +298,7 @@ module ActiveMerchant #:nodoc:
298
298
  xml.ssl_dynamic_dba options[:dba] if options.has_key?(:dba)
299
299
  xml.ssl_merchant_initiated_unscheduled merchant_initiated_unscheduled(options) if merchant_initiated_unscheduled(options)
300
300
  xml.ssl_add_token options[:add_recurring_token] if options.has_key?(:add_recurring_token)
301
- xml.ssl_token options[:ssl_token] if options.has_key?(:ssl_token)
301
+ xml.ssl_token options[:ssl_token] if options[:ssl_token]
302
302
  xml.ssl_customer_code options[:customer] if options.has_key?(:customer)
303
303
  xml.ssl_customer_number options[:customer_number] if options.has_key?(:customer_number)
304
304
  xml.ssl_entry_mode entry_mode(options) if entry_mode(options)
@@ -393,6 +393,7 @@ module ActiveMerchant #:nodoc:
393
393
 
394
394
  def commit(request)
395
395
  request = "xmldata=#{request}".delete('&')
396
+ store_action = request.match?('CCGETTOKEN')
396
397
 
397
398
  response = parse(ssl_post(test? ? self.test_url : self.live_url, request, headers))
398
399
  response = hash_html_decode(response)
@@ -402,7 +403,7 @@ module ActiveMerchant #:nodoc:
402
403
  response[:result_message] || response[:errorMessage],
403
404
  response,
404
405
  test: @options[:test] || test?,
405
- authorization: authorization_from(response),
406
+ authorization: authorization_from(response, store_action),
406
407
  error_code: response[:errorCode],
407
408
  avs_result: { code: response[:avs_response] },
408
409
  cvv_result: response[:cvv2_response],
@@ -428,7 +429,9 @@ module ActiveMerchant #:nodoc:
428
429
  response.deep_transform_keys { |key| key.gsub('ssl_', '').to_sym }
429
430
  end
430
431
 
431
- def authorization_from(response)
432
+ def authorization_from(response, store_action)
433
+ return response[:token] if store_action
434
+
432
435
  [response[:approval_code], response[:txn_id]].join(';')
433
436
  end
434
437
 
@@ -82,6 +82,19 @@ module ActiveMerchant #:nodoc:
82
82
  commit('CreditCardReturn', request, money)
83
83
  end
84
84
 
85
+ def credit(money, payment, options = {})
86
+ request = build_soap_request do |xml|
87
+ xml.CreditCardCredit(xmlns: 'https://transaction.elementexpress.com') do
88
+ add_credentials(xml)
89
+ add_payment_method(xml, payment)
90
+ add_transaction(xml, money, options)
91
+ add_terminal(xml, options)
92
+ end
93
+ end
94
+
95
+ commit('CreditCardCredit', request, money)
96
+ end
97
+
85
98
  def void(authorization, options = {})
86
99
  trans_id, trans_amount = split_authorization(authorization)
87
100
  options.merge!({ trans_id: trans_id, trans_amount: trans_amount, reversal_type: 'Full' })
@@ -186,9 +199,10 @@ module ActiveMerchant #:nodoc:
186
199
  xml.ReversalType options[:reversal_type] if options[:reversal_type]
187
200
  xml.TransactionID options[:trans_id] if options[:trans_id]
188
201
  xml.TransactionAmount amount(money.to_i) if money
189
- xml.MarketCode 'Default' if money
202
+ xml.MarketCode market_code(money, options) if options[:market_code] || money
190
203
  xml.ReferenceNumber options[:order_id] || SecureRandom.hex(20)
191
-
204
+ xml.TicketNumber options[:ticket_number] if options[:ticket_number]
205
+ xml.MerchantSuppliedTransactionId options[:merchant_supplied_transaction_id] if options[:merchant_supplied_transaction_id]
192
206
  xml.PaymentType options[:payment_type] if options[:payment_type]
193
207
  xml.SubmissionType options[:submission_type] if options[:submission_type]
194
208
  xml.DuplicateCheckDisableFlag options[:duplicate_check_disable_flag].to_s == 'true' ? 'True' : 'False' unless options[:duplicate_check_disable_flag].nil?
@@ -197,6 +211,10 @@ module ActiveMerchant #:nodoc:
197
211
  end
198
212
  end
199
213
 
214
+ def market_code(money, options)
215
+ options[:market_code] || 'Default'
216
+ end
217
+
200
218
  def add_terminal(xml, options)
201
219
  xml.terminal do
202
220
  xml.TerminalID options[:terminal_id] || '01'