activemerchant 1.94.0 → 1.99.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 (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
@@ -2,6 +2,8 @@ require 'active_support/core_ext/hash/slice'
2
2
 
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
+ # This gateway uses an older version of the Stripe API.
6
+ # To utilize the updated {Payment Intents API}[https://stripe.com/docs/api/payment_intents], integrate with the StripePaymentIntents gateway
5
7
  class StripeGateway < Gateway
6
8
  self.live_url = 'https://api.stripe.com/v1/'
7
9
 
@@ -21,7 +23,9 @@ module ActiveMerchant #:nodoc:
21
23
  'unchecked' => 'P'
22
24
  }
23
25
 
24
- self.supported_countries = %w(AT AU BE BR CA CH DE DK ES FI FR GB HK IE IT JP LU MX NL NO NZ PT SE SG US)
26
+ DEFAULT_API_VERSION = '2015-04-07'
27
+
28
+ self.supported_countries = %w(AT AU BE BR CA CH DE DK EE ES FI FR GB GR HK IE IT JP LT LU LV MX NL NO NZ PL PT SE SG SI SK US)
25
29
  self.default_currency = 'USD'
26
30
  self.money_format = :cents
27
31
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club, :maestro]
@@ -300,8 +304,8 @@ module ActiveMerchant #:nodoc:
300
304
  add_amount(post, money, options, true)
301
305
  post[:type] = type
302
306
  if type == 'card'
303
- add_creditcard(post, payment, options)
304
- post[:card].delete(:name)
307
+ add_creditcard(post, payment, options, true)
308
+ add_source_owner(post, payment, options)
305
309
  elsif type == 'three_d_secure'
306
310
  post[:three_d_secure] = {card: payment}
307
311
  post[:redirect] = {return_url: options[:redirect_url]}
@@ -347,6 +351,12 @@ module ActiveMerchant #:nodoc:
347
351
  add_creditcard(post, payment, options)
348
352
  end
349
353
 
354
+ add_charge_details(post, money, payment, options)
355
+ post
356
+ end
357
+
358
+ # Used internally by Spreedly to populate the charge object for 3DS 1.0 transactions
359
+ def add_charge_details(post, money, payment, options)
350
360
  if emv_payment?(payment)
351
361
  add_statement_address(post, options)
352
362
  add_emv_metadata(post, payment)
@@ -449,7 +459,7 @@ module ActiveMerchant #:nodoc:
449
459
  post[:statement_address][:state] = statement_address[:state]
450
460
  end
451
461
 
452
- def add_creditcard(post, creditcard, options)
462
+ def add_creditcard(post, creditcard, options, use_sources = false)
453
463
  card = {}
454
464
  if emv_payment?(creditcard)
455
465
  add_emv_creditcard(post, creditcard.icc_data)
@@ -472,7 +482,7 @@ module ActiveMerchant #:nodoc:
472
482
  card[:exp_month] = creditcard.month
473
483
  card[:exp_year] = creditcard.year
474
484
  card[:cvc] = creditcard.verification_value if creditcard.verification_value?
475
- card[:name] = creditcard.name if creditcard.name
485
+ card[:name] = creditcard.name if creditcard.name && !use_sources
476
486
  end
477
487
 
478
488
  if creditcard.is_a?(NetworkTokenizationCreditCard)
@@ -482,7 +492,7 @@ module ActiveMerchant #:nodoc:
482
492
  end
483
493
  post[:card] = card
484
494
 
485
- add_address(post, options)
495
+ add_address(post, options) unless use_sources
486
496
  elsif creditcard.kind_of?(String)
487
497
  if options[:track_data]
488
498
  card[:swipe_data] = options[:track_data]
@@ -530,6 +540,25 @@ module ActiveMerchant #:nodoc:
530
540
  post[:metadata][:card_read_method] = creditcard.read_method if creditcard.respond_to?(:read_method)
531
541
  end
532
542
 
543
+ def add_source_owner(post, creditcard, options)
544
+ post[:owner] = {}
545
+ post[:owner][:name] = creditcard.name if creditcard.name
546
+ post[:owner][:email] = options[:email] if options[:email]
547
+
548
+ if address = options[:billing_address] || options[:address]
549
+ owner_address = {}
550
+ owner_address[:line1] = address[:address1] if address[:address1]
551
+ owner_address[:line2] = address[:address2] if address[:address2]
552
+ owner_address[:country] = address[:country] if address[:country]
553
+ owner_address[:postal_code] = address[:zip] if address[:zip]
554
+ owner_address[:state] = address[:state] if address[:state]
555
+ owner_address[:city] = address[:city] if address[:city]
556
+
557
+ post[:owner][:phone] = address[:phone] if address[:phone]
558
+ post[:owner][:address] = owner_address
559
+ end
560
+ end
561
+
533
562
  def parse(body)
534
563
  JSON.parse(body)
535
564
  end
@@ -589,7 +618,7 @@ module ActiveMerchant #:nodoc:
589
618
  end
590
619
 
591
620
  def api_version(options)
592
- options[:version] || @options[:version] || '2015-04-07'
621
+ options[:version] || @options[:version] || self.class::DEFAULT_API_VERSION
593
622
  end
594
623
 
595
624
  def api_request(method, endpoint, parameters = nil, options = {})
@@ -632,8 +661,8 @@ module ActiveMerchant #:nodoc:
632
661
  return response.fetch('error', {})['charge'] unless success
633
662
 
634
663
  if url == 'customers'
635
- [response['id'], response['sources']['data'].first['id']].join('|')
636
- elsif method == :post && url.match(/customers\/.*\/cards/)
664
+ [response['id'], response.dig('sources', 'data').first&.dig('id')].join('|')
665
+ elsif method == :post && (url.match(/customers\/.*\/cards/) || url.match(/payment_methods\/.*\/attach/))
637
666
  [response['customer'], response['id']].join('|')
638
667
  else
639
668
  response['id']
@@ -0,0 +1,271 @@
1
+ require 'active_support/core_ext/hash/slice'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # This gateway uses the current Stripe {Payment Intents API}[https://stripe.com/docs/api/payment_intents].
6
+ # For the legacy API, see the Stripe gateway
7
+ class StripePaymentIntentsGateway < StripeGateway
8
+
9
+ self.supported_countries = %w(AT AU BE BR CA CH DE DK EE ES FI FR GB GR HK IE IT JP LT LU LV MX NL NO NZ PL PT SE SG SI SK US)
10
+
11
+ ALLOWED_METHOD_STATES = %w[automatic manual].freeze
12
+ ALLOWED_CANCELLATION_REASONS = %w[duplicate fraudulent requested_by_customer abandoned].freeze
13
+ CREATE_INTENT_ATTRIBUTES = %i[description statement_descriptor receipt_email save_payment_method]
14
+ CONFIRM_INTENT_ATTRIBUTES = %i[receipt_email return_url save_payment_method setup_future_usage off_session]
15
+ UPDATE_INTENT_ATTRIBUTES = %i[description statement_descriptor receipt_email setup_future_usage]
16
+ DEFAULT_API_VERSION = '2019-05-16'
17
+
18
+ def create_intent(money, payment_method, options = {})
19
+ post = {}
20
+ add_amount(post, money, options, true)
21
+ add_capture_method(post, options)
22
+ add_confirmation_method(post, options)
23
+ add_customer(post, options)
24
+ add_payment_method_token(post, payment_method, options)
25
+ add_metadata(post, options)
26
+ add_return_url(post, options)
27
+ add_connected_account(post, options)
28
+ add_shipping_address(post, options)
29
+ setup_future_usage(post, options)
30
+ add_exemption(post, options)
31
+
32
+ CREATE_INTENT_ATTRIBUTES.each do |attribute|
33
+ add_whitelisted_attribute(post, options, attribute)
34
+ end
35
+
36
+ commit(:post, 'payment_intents', post, options)
37
+ end
38
+
39
+ def show_intent(intent_id, options)
40
+ commit(:get, "payment_intents/#{intent_id}", nil, options)
41
+ end
42
+
43
+ def confirm_intent(intent_id, payment_method, options = {})
44
+ post = {}
45
+ add_payment_method_token(post, payment_method, options)
46
+ CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
47
+ add_whitelisted_attribute(post, options, attribute)
48
+ end
49
+
50
+ commit(:post, "payment_intents/#{intent_id}/confirm", post, options)
51
+ end
52
+
53
+ def create_payment_method(payment_method, options = {})
54
+ post = {}
55
+ post[:type] = 'card'
56
+ post[:card] = {}
57
+ post[:card][:number] = payment_method.number
58
+ post[:card][:exp_month] = payment_method.month
59
+ post[:card][:exp_year] = payment_method.year
60
+ post[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
61
+ add_billing_address(post, options)
62
+
63
+ commit(:post, 'payment_methods', post, options)
64
+ end
65
+
66
+ def update_intent(money, intent_id, payment_method, options = {})
67
+ post = {}
68
+ post[:amount] = money if money
69
+
70
+ add_payment_method_token(post, payment_method, options)
71
+ add_payment_method_types(post, options)
72
+ add_customer(post, options)
73
+ add_metadata(post, options)
74
+ add_shipping_address(post, options)
75
+ add_connected_account(post, options)
76
+
77
+ UPDATE_INTENT_ATTRIBUTES.each do |attribute|
78
+ add_whitelisted_attribute(post, options, attribute)
79
+ end
80
+
81
+ commit(:post, "payment_intents/#{intent_id}", post, options)
82
+ end
83
+
84
+ def authorize(money, payment_method, options = {})
85
+ create_intent(money, payment_method, options.merge!(confirm: true, capture_method: 'manual'))
86
+ end
87
+
88
+ def purchase(money, payment_method, options = {})
89
+ create_intent(money, payment_method, options.merge!(confirm: true, capture_method: 'automatic'))
90
+ end
91
+
92
+ def capture(money, intent_id, options = {})
93
+ post = {}
94
+ post[:amount_to_capture] = money
95
+ if options[:transfer_amount]
96
+ post[:transfer_data] = {}
97
+ post[:transfer_data][:amount] = options[:transfer_amount]
98
+ end
99
+ post[:application_fee_amount] = options[:application_fee] if options[:application_fee]
100
+ commit(:post, "payment_intents/#{intent_id}/capture", post, options)
101
+ end
102
+
103
+ def void(intent_id, options = {})
104
+ post = {}
105
+ post[:cancellation_reason] = options[:cancellation_reason] if ALLOWED_CANCELLATION_REASONS.include?(options[:cancellation_reason])
106
+ commit(:post, "payment_intents/#{intent_id}/cancel", post, options)
107
+ end
108
+
109
+ def refund(money, intent_id, options = {})
110
+ intent = commit(:get, "payment_intents/#{intent_id}", nil, options)
111
+ charge_id = intent.params.dig('charges', 'data')[0].dig('id')
112
+ super(money, charge_id, options)
113
+ end
114
+
115
+ # Note: Not all payment methods are currently supported by the {Payment Methods API}[https://stripe.com/docs/payments/payment-methods]
116
+ # Current implementation will create a PaymentMethod object if the method is a token or credit card
117
+ # All other types will default to legacy Stripe store
118
+ def store(payment_method, options = {})
119
+ params = {}
120
+ post = {}
121
+
122
+ # If customer option is provided, create a payment method and attach to customer id
123
+ # Otherwise, create a customer, then attach
124
+ if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
125
+ add_payment_method_token(params, payment_method, options)
126
+ if options[:customer]
127
+ customer_id = options[:customer]
128
+ else
129
+ post[:validate] = options[:validate] unless options[:validate].nil?
130
+ post[:description] = options[:description] if options[:description]
131
+ post[:email] = options[:email] if options[:email]
132
+ customer = commit(:post, 'customers', post, options)
133
+ customer_id = customer.params['id']
134
+ end
135
+ commit(:post, "payment_methods/#{params[:payment_method]}/attach", { customer: customer_id }, options)
136
+ else
137
+ super(payment, options)
138
+ end
139
+ end
140
+
141
+ def unstore(identification, options = {}, deprecated_options = {})
142
+ if identification.include?('pm_')
143
+ _, payment_method = identification.split('|')
144
+ commit(:post, "payment_methods/#{payment_method}/detach", nil, options)
145
+ else
146
+ super(identification, options, deprecated_options)
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def add_whitelisted_attribute(post, options, attribute)
153
+ post[attribute] = options[attribute] if options[attribute]
154
+ post
155
+ end
156
+
157
+ def add_capture_method(post, options)
158
+ capture_method = options[:capture_method].to_s
159
+ post[:capture_method] = capture_method if ALLOWED_METHOD_STATES.include?(capture_method)
160
+ post
161
+ end
162
+
163
+ def add_confirmation_method(post, options)
164
+ confirmation_method = options[:confirmation_method].to_s
165
+ post[:confirmation_method] = confirmation_method if ALLOWED_METHOD_STATES.include?(confirmation_method)
166
+ post
167
+ end
168
+
169
+ def add_customer(post, options)
170
+ customer = options[:customer].to_s
171
+ post[:customer] = customer if customer.start_with?('cus_')
172
+ post
173
+ end
174
+
175
+ def add_return_url(post, options)
176
+ return unless options[:confirm]
177
+ post[:confirm] = options[:confirm]
178
+ post[:return_url] = options[:return_url] if options[:return_url]
179
+ post
180
+ end
181
+
182
+ def add_payment_method_token(post, payment_method, options)
183
+ return if payment_method.nil?
184
+
185
+ if payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
186
+ p = create_payment_method(payment_method, options)
187
+ payment_method = p.params['id']
188
+ end
189
+
190
+ if payment_method.is_a?(StripePaymentToken)
191
+ post[:payment_method] = payment_method.payment_data['id']
192
+ elsif payment_method.is_a?(String)
193
+ if payment_method.include?('|')
194
+ customer_id, payment_method_id = payment_method.split('|')
195
+ token = payment_method_id
196
+ post[:customer] = customer_id
197
+ else
198
+ token = payment_method
199
+ end
200
+ post[:payment_method] = token
201
+ end
202
+ end
203
+
204
+ def add_payment_method_types(post, options)
205
+ payment_method_types = options[:payment_method_types] if options[:payment_method_types]
206
+ return if payment_method_types.nil?
207
+
208
+ post[:payment_method_types] = Array(payment_method_types)
209
+ post
210
+ end
211
+
212
+ def add_exemption(post, options = {})
213
+ return unless options[:confirm]
214
+ post[:payment_method_options] ||= {}
215
+ post[:payment_method_options][:card] ||= {}
216
+ post[:payment_method_options][:card][:moto] = true if options[:moto]
217
+ end
218
+
219
+ def setup_future_usage(post, options = {})
220
+ post[:setup_future_usage] = options[:setup_future_usage] if %w( on_session off_session ).include?(options[:setup_future_usage])
221
+ post[:off_session] = options[:off_session] if options[:off_session] && options[:confirm] == true
222
+ post
223
+ end
224
+
225
+ def add_connected_account(post, options = {})
226
+ return unless options[:transfer_destination]
227
+ post[:transfer_data] = {}
228
+ post[:transfer_data][:destination] = options[:transfer_destination]
229
+ post[:transfer_data][:amount] = options[:transfer_amount] if options[:transfer_amount]
230
+ post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
231
+ post[:transfer_group] = options[:transfer_group] if options[:transfer_group]
232
+ post[:application_fee_amount] = options[:application_fee] if options[:application_fee]
233
+ post
234
+ end
235
+
236
+ def add_billing_address(post, options = {})
237
+ return unless billing = options[:billing_address] || options[:address]
238
+ post[:billing_details] = {}
239
+ post[:billing_details][:address] = {}
240
+ post[:billing_details][:address][:city] = billing[:city] if billing[:city]
241
+ post[:billing_details][:address][:country] = billing[:country] if billing[:country]
242
+ post[:billing_details][:address][:line1] = billing[:address1] if billing[:address1]
243
+ post[:billing_details][:address][:line2] = billing[:address2] if billing[:address2]
244
+ post[:billing_details][:address][:postal_code] = billing[:zip] if billing[:zip]
245
+ post[:billing_details][:address][:state] = billing[:state] if billing[:state]
246
+ post[:billing_details][:email] = billing[:email] if billing[:email]
247
+ post[:billing_details][:name] = billing[:name] if billing[:name]
248
+ post[:billing_details][:phone] = billing[:phone] if billing[:phone]
249
+ post
250
+ end
251
+
252
+ def add_shipping_address(post, options = {})
253
+ return unless shipping = options[:shipping]
254
+ post[:shipping] = {}
255
+ post[:shipping][:address] = {}
256
+ post[:shipping][:address][:line1] = shipping[:address][:line1]
257
+ post[:shipping][:address][:city] = shipping[:address][:city] if shipping[:address][:city]
258
+ post[:shipping][:address][:country] = shipping[:address][:country] if shipping[:address][:country]
259
+ post[:shipping][:address][:line2] = shipping[:address][:line2] if shipping[:address][:line2]
260
+ post[:shipping][:address][:postal_code] = shipping[:address][:postal_code] if shipping[:address][:postal_code]
261
+ post[:shipping][:address][:state] = shipping[:address][:state] if shipping[:address][:state]
262
+
263
+ post[:shipping][:name] = shipping[:name]
264
+ post[:shipping][:carrier] = shipping[:carrier] if shipping[:carrier]
265
+ post[:shipping][:phone] = shipping[:phone] if shipping[:phone]
266
+ post[:shipping][:tracking_number] = shipping[:tracking_number] if shipping[:tracking_number]
267
+ post
268
+ end
269
+ end
270
+ end
271
+ end
@@ -3,13 +3,18 @@ module ActiveMerchant
3
3
  class TnsGateway < Gateway
4
4
  include MastercardGateway
5
5
 
6
- class_attribute :live_na_url, :live_ap_url, :test_na_url, :test_ap_url
6
+ class_attribute :live_na_url, :live_ap_url, :live_eu_url, :test_na_url, :test_ap_url, :test_eu_url
7
7
 
8
- self.live_na_url = 'https://secure.na.tnspayments.com/api/rest/version/36/'
9
- self.test_na_url = 'https://secure.na.tnspayments.com/api/rest/version/36/'
8
+ VERSION = '52'
10
9
 
11
- self.live_ap_url = 'https://secure.ap.tnspayments.com/api/rest/version/36/'
12
- self.test_ap_url = 'https://secure.ap.tnspayments.com/api/rest/version/36/'
10
+ self.live_na_url = "https://secure.na.tnspayments.com/api/rest/version/#{VERSION}/"
11
+ self.test_na_url = "https://secure.na.tnspayments.com/api/rest/version/#{VERSION}/"
12
+
13
+ self.live_ap_url = "https://secure.ap.tnspayments.com/api/rest/version/#{VERSION}/"
14
+ self.test_ap_url = "https://secure.ap.tnspayments.com/api/rest/version/#{VERSION}/"
15
+
16
+ self.live_eu_url = "https://secure.eu.tnspayments.com/api/rest/version/#{VERSION}/"
17
+ self.test_eu_url = "https://secure.eu.tnspayments.com/api/rest/version/#{VERSION}/"
13
18
 
14
19
  self.display_name = 'TNS'
15
20
  self.homepage_url = 'http://www.tnsi.com/'
@@ -544,7 +544,7 @@ module ActiveMerchant #:nodoc:
544
544
  end
545
545
  end
546
546
  doc['v1'].addrLn1 billing_address[:address1] if billing_address[:address1]
547
- doc['v1'].addrLn2 billing_address[:address2] if billing_address[:address2]
547
+ doc['v1'].addrLn2 billing_address[:address2] unless billing_address[:address2].blank?
548
548
  doc['v1'].city billing_address[:city] if billing_address[:city]
549
549
  doc['v1'].state billing_address[:state] if billing_address[:state]
550
550
  doc['v1'].zipCode billing_address[:zip] if billing_address[:zip]
@@ -559,7 +559,7 @@ module ActiveMerchant #:nodoc:
559
559
  doc['v1'].ship do
560
560
  doc['v1'].fullName fullname unless fullname.blank?
561
561
  doc['v1'].addrLn1 shipping_address[:address1] if shipping_address[:address1]
562
- doc['v1'].addrLn2 shipping_address[:address2] if shipping_address[:address2]
562
+ doc['v1'].addrLn2 shipping_address[:address2] unless shipping_address[:address2].blank?
563
563
  doc['v1'].city shipping_address[:city] if shipping_address[:city]
564
564
  doc['v1'].state shipping_address[:state] if shipping_address[:state]
565
565
  doc['v1'].zipCode shipping_address[:zip] if shipping_address[:zip]
@@ -104,6 +104,8 @@ module ActiveMerchant #:nodoc:
104
104
  TEST_LOGIN = 'TestMerchant'
105
105
  TEST_PASSWORD = 'password'
106
106
 
107
+ VOIDABLE_ACTIONS = %w(preauth sale postauth credit)
108
+
107
109
  self.money_format = :cents
108
110
  self.supported_cardtypes = [:visa, :master, :discover, :american_express, :diners_club, :jcb]
109
111
  self.supported_countries = ['US']
@@ -157,6 +159,8 @@ module ActiveMerchant #:nodoc:
157
159
  add_customer_data(parameters, options)
158
160
  add_payment_source(parameters, creditcard_or_billing_id)
159
161
  add_addresses(parameters, options)
162
+ add_custom_fields(parameters, options)
163
+
160
164
  commit('preauth', parameters)
161
165
  end
162
166
 
@@ -172,6 +176,8 @@ module ActiveMerchant #:nodoc:
172
176
  add_customer_data(parameters, options)
173
177
  add_payment_source(parameters, creditcard_or_billing_id)
174
178
  add_addresses(parameters, options)
179
+ add_custom_fields(parameters, options)
180
+
175
181
  commit('sale', parameters)
176
182
  end
177
183
 
@@ -179,11 +185,13 @@ module ActiveMerchant #:nodoc:
179
185
  # postauth, we preserve active_merchant's nomenclature of capture() for consistency with the rest of the library. To process
180
186
  # a postauthorization with TC, you need an amount in cents or a money object, and a TC transid.
181
187
  def capture(money, authorization, options = {})
188
+ transaction_id, _ = split_authorization(authorization)
182
189
  parameters = {
183
190
  :amount => amount(money),
184
- :transid => authorization,
191
+ :transid => transaction_id,
185
192
  }
186
193
  add_aggregator(parameters, options)
194
+ add_custom_fields(parameters, options)
187
195
 
188
196
  commit('postauth', parameters)
189
197
  end
@@ -191,11 +199,15 @@ module ActiveMerchant #:nodoc:
191
199
  # refund() allows you to return money to a card that was previously billed. You need to supply the amount, in cents or a money object,
192
200
  # that you want to refund, and a TC transid for the transaction that you are refunding.
193
201
  def refund(money, identification, options = {})
202
+ transaction_id, _ = split_authorization(identification)
203
+
194
204
  parameters = {
195
205
  :amount => amount(money),
196
- :transid => identification
206
+ :transid => transaction_id
197
207
  }
208
+
198
209
  add_aggregator(parameters, options)
210
+ add_custom_fields(parameters, options)
199
211
 
200
212
  commit('credit', parameters)
201
213
  end
@@ -214,18 +226,26 @@ module ActiveMerchant #:nodoc:
214
226
  # TrustCommerce to allow for reversal transactions before you can use this
215
227
  # method.
216
228
  #
229
+ # void() is also used to to cancel a capture (postauth), purchase (sale),
230
+ # or refund (credit) or a before it is sent for settlement.
231
+ #
217
232
  # NOTE: AMEX preauth's cannot be reversed. If you want to clear it more
218
233
  # quickly than the automatic expiration (7-10 days), you will have to
219
234
  # capture it and then immediately issue a credit for the same amount
220
235
  # which should clear the customers credit card with 48 hours according to
221
236
  # TC.
222
237
  def void(authorization, options = {})
238
+ transaction_id, original_action = split_authorization(authorization)
239
+ action = (VOIDABLE_ACTIONS - ['preauth']).include?(original_action) ? 'void' : 'reversal'
240
+
223
241
  parameters = {
224
- :transid => authorization,
242
+ :transid => transaction_id,
225
243
  }
244
+
226
245
  add_aggregator(parameters, options)
246
+ add_custom_fields(parameters, options)
227
247
 
228
- commit('reversal', parameters)
248
+ commit(action, parameters)
229
249
  end
230
250
 
231
251
  # recurring() a TrustCommerce account that is activated for Citadel, TrustCommerce's
@@ -284,6 +304,8 @@ module ActiveMerchant #:nodoc:
284
304
 
285
305
  add_creditcard(parameters, creditcard)
286
306
  add_addresses(parameters, options)
307
+ add_custom_fields(parameters, options)
308
+
287
309
  commit('store', parameters)
288
310
  end
289
311
 
@@ -294,6 +316,8 @@ module ActiveMerchant #:nodoc:
294
316
  :billingid => identification,
295
317
  }
296
318
 
319
+ add_custom_fields(parameters, options)
320
+
297
321
  commit('unstore', parameters)
298
322
  end
299
323
 
@@ -311,6 +335,12 @@ module ActiveMerchant #:nodoc:
311
335
 
312
336
  private
313
337
 
338
+ def add_custom_fields(params, options)
339
+ options[:custom_fields]&.each do |key, value|
340
+ params[key.to_sym] = value
341
+ end
342
+ end
343
+
314
344
  def add_aggregator(params, options)
315
345
  if @options[:aggregator_id] || application_id != Gateway.application_id
316
346
  params[:aggregators] = 1
@@ -409,14 +439,14 @@ module ActiveMerchant #:nodoc:
409
439
  TCLink.send(parameters)
410
440
  else
411
441
  parse(ssl_post(self.live_url, post_data(parameters)))
412
- end
442
+ end
413
443
 
414
444
  # to be considered successful, transaction status must be either "approved" or "accepted"
415
445
  success = SUCCESS_TYPES.include?(data['status'])
416
446
  message = message_from(data)
417
447
  Response.new(success, message, data,
418
448
  :test => test?,
419
- :authorization => data['transid'],
449
+ :authorization => authorization_from(action, data),
420
450
  :cvv_result => data['cvv'],
421
451
  :avs_result => { :code => data['avs'] }
422
452
  )
@@ -446,6 +476,15 @@ module ActiveMerchant #:nodoc:
446
476
  end
447
477
  end
448
478
 
479
+ def authorization_from(action, data)
480
+ authorization = data['transid']
481
+ authorization = "#{authorization}|#{action}" if authorization && VOIDABLE_ACTIONS.include?(action)
482
+ authorization
483
+ end
484
+
485
+ def split_authorization(authorization)
486
+ authorization&.split('|')
487
+ end
449
488
  end
450
489
  end
451
490
  end